Illustrative examples for the following discussions can be found here.
-
It is suggested that you also provide a default constructor if you ever provide a user-defined constructor when writing a C++ class.
-
If you want to allow deleting a derived class from a pointer of the base class, you have to make the destructor of the base class
public
andvirtual
even if it does nothing. If you want to prevent deleting a derived class from a pointer of the base class, make the descturctor of the base classprotected
andvirtual
. -
If you derive a class from a base class, the destructor of the base class is called automatically so that you do not have to call it manually.
-
If a derived class
D
has a method with the namefun
, then all non-virtual methods with the namefun
(no matter what signature they have) in its base classB
arehidden
by the methodfun
inD
. Supposed
andb
are instances of the classD
andB
respectively, and you invoke the methodfun
throught these two instances, then everything works OK (in the sense that theright
method is invoked). However, this is different fromoverridding
. The problem is thathidden
does not support polymorphism. If you have a pointer of the base classB
pointing to an instance of the derived classD
, and you invoke the method via the pointer, thenfun
of the base classB
will be called. To support polymophism, you have tooverride
the methodfun
in the base classB
. To do this, you have to markfun
as virtual in the base class. To help the compiler (and also make your code more readable), you can useoverride
after the signature of method explicitly. Here is an example illustrating problems discussed above. -
Overriding method must have the same return type as the overrided method. (not sure whether this is required in Java)
-
If you write your own version constructor for a class, you'd better also provide the copy/move constructor and assignment operator.
-
You'd better not use lvalue references in a class that point to an object outside the class. A better way is to pass the object by lvalue reference to methods that need it. If you ever decide to use a lvalue reference in a class that point to an object outside the class, you must initialize it in the initialization list of a constructor. This is because when you initialize a lvalue reference, you must point it to a valide object.
-
Generally speaking, member variables should not be declared as public, unless they are
static constant
. Also you should not declare member variables asprotected
unless you are sure that the derived classes want to access these member variables directly. If you define a proteced memberm
in a base classB
, a derived classD
can access and modify (ifm
is a data member)m
in its own class or throught its own instances but not throught other arbitrary object. For example, ifb
is an object of the base classB
, you cannot access or modifyb.m
directly in the definition of the derived classD
. -
It is suggested that you provide a
to_string
method, rather than overridding the<<
operator. -
You'd better not use overloading and default parameters at the same time. Because this might make compiler fail to find the right version of function/method to call. Generally speaking, overloading is more powerful and thus prefereed.
-
Inheritance works with template class, i.e., you can derive class from template classes.
-
A
swap
method can make the implementation of copy and move structor unified and thus more conveneint. It is suggested that you make theswap
functionpublic
(at leastprotected
) if you provide one. -
If a class have many variables to be initialized, you can think of intializing non-critical variables in the definition of the class and provide public method to access and modify them. This make it easier to implement constructors.
-
Always mark a method as
const
if it does not change the state of the object. -
It is suggested that you intialize member variables in the constructor initializeing list (after
:
before{
). Surely you can achieve the same by assigning values to member variables in the body of constructors, but in this way these member variables are first initialized to default values and then assigned values, which is not as efficient.
Virtuality
The following are some guidelines for virtuality from Herb Sutter. The original article can be found here.
-
Prefer to make interfaces nonvirtual, using Template Method.
-
Prefer to make virtual functions private.
-
Only if derived classes need to invoke the base implementation of a virtual function, make the virtual function protected.
-
A base class destructor should be either public and virtual, or protected and nonvirtual.
A few points to add:
-
A virtual method can be overridden by a derived class even if it is private.
-
A class without a public destructor cannot be used by itself. It servers as a base class only.