When programming such a class, you need to clearly define the behavior in the scenario where an instance is being copied directly into another via assignment or copied indirectly by being passed to a function by value. You typically implement the copy constructor, copy assignment operator, and the destructor. You also implement the move constructor and move assignment operator if you want to tweak the performance of this class in certain cases. To enable an array-like access to elements stored inside an instance of the class, you would want to overload the subscript operator.
A smart pointer needs to supply the ability of being used as a normal pointer as in *pSmartPtr or pSmartPtr->Func(). To enable this you implement operator (*) and operator (->). In addition, for it to be smart, you also take care of automatic resource release/returns by programming the destructor accordingly, and you would clearly define how copy or assignment works by implementing the copy constructor and copy assignment operator or by prohibiting it by declaring these two as private.
const Type& operator(int index);
Type& operator(int index); // is this OK?
No, C++ does not allow two functions with the same name to have different return
values. You can program two implementations of operator  with identical return
types, one defined as a const function and the other not. In this case, C++ compiler
picks the non-const version for assignment-related activities and the const
Type& operator(int Index) const;
Type& operator(int Index);
Yes, but only if I don't want my class to allow copying or assignment. Such a restriction would be necessity when programming a singleton-a class that permits the existence of only one instance.
As there are no dynamically allocated resources contained within class Date that cause unnecessary memory allocation and deallocation cycles within the copy constructor or copy assignment operator, this class is not a good candidate for a move constructor or move assignment operator.
Most definitely not. The result of such an operation is not defined and is definitely not desired.
Again, definitely not. reinterpret_cast changed only the interpretation of the pointer, and did not change the object being pointed to (that is still a Dog). Calling a Fly() function on a Dog object will not give the results you are looking for, and could possibly cause an application failure.
Because you are sure that the object being pointed to is a Derived type, you can save on runtime performance by using static_cast.
You keep aspirin at home, but you don't make it your staple diet just because it's available, right? Use casts only when you need them.
Inclusion guards using #ifndef, #define, and #endif protect your header from multiple or recursive inclusion errors, and in some cases they even speed up compilation.