Constructor is a special type of member function in OOP used to create objects and initialize member variables. In C++, constructors are similar to classes and do not have a return value.
Custom constructors are needed in the following situations:
- Custom initialization of class members
- Calling functions when creating objects of a class
Constructors are generally declared as public
, but can also be declared as protected
, private
, etc.
Default Constructor#
A default constructor does not provide any parameters and can be implicitly defined when the class is defined. When not explicitly defined, the compiler will generate a default constructor. An example of a default constructor is:
class MyClass{
public:
MyClass(){
// do something..
}
}
The "Most Vexing Parse" about Default Constructors#
Because C++'s parsing program tends to lean towards declarations, there will be a warning when trying to use parentheses to call the generated default constructor:
class MyClass{};
int main(){
MyClass myClass();
// warning C4930
}
In this example, MyClass myClass();
does not call the default constructor of MyClass
to create the myClass
object as expected. Instead, this line of code is actually interpreted as a function declaration, not an object declaration. Here, myClass
is interpreted as a function that takes no arguments and returns an object of type MyClass
.
This phenomenon is known as the "Most Vexing Parse," a famous syntax parsing problem in the C++ language. This problem arises from the complexity of C++ syntax, where certain syntax structures can be interpreted as multiple different things depending on the context. In this case, the compiler prioritizes interpreting the expression as a function declaration rather than an object construction.
To avoid this problem, explicitly create the object:
int main() {
MyClass myClass;
}
Parameterized Constructor#
A parameterized constructor initializes with multiple parameters:
class MyClass{
public:
int x, y;
MyClass(int a, int b): x(a), y(b){
// do something..
}
}
Here, the member initialization list is used, with the expression taking the form:
identifier(argument)
The member initialization list consists of all expressions after the colon:
MyFunc(int a, int b, int c):
m_a(a), m_b(b), m_c(c){}
Copy Constructor#
A copy constructor is used to initialize a new object based on an object of the same type, usually in object copying scenarios:
class MyClass{
public:
int x, y;
MyClass(const MyClass& mc):
x(mc.x), y(mc.y) {}
}
In the simple example above, the members are scalars, so the compiler is sufficient to generate the copy constructor without needing to define it yourself. However, when the copy constructor needs to implement special behavior, such as allocating new memory, you need to implement the copy constructor yourself.
When defining constructors, the copy assignment operator =
needs to be overloaded.
Move Constructor#
The move constructor is a new feature introduced after C++11. It is used to transfer resources from one object to another, usually in scenarios that support rvalue references. It transfers ownership of the existing object's data to the new variable without copying the original data. It takes an rvalue reference as its first parameter, and any subsequent parameters must have default values. The move constructor can significantly improve program efficiency when passing large objects:
// todo lvalues and rvalues
class MyClass{
public:
char *str;
MyClass(MyClass && obj): str(obj.str){
obj.str = nullptr; // Avoid releasing memory when the original object is destructed
}
}
Delegating Constructor#
Delegating constructors are also a new feature introduced after C++11. They allow one constructor to call another constructor within the same class, further decoupling the code:
class MyClass{
public:
int x, y, z;
MyClass(int a, int b):x(a), y(b), z(0) {}
MyClass(int a, int b, int c): Point(a, b) {z = c;}
}
Inheriting Constructor#
Inheriting constructors are a concept introduced after C++11. It allows constructors to be inherited from a base class using the using
keyword:
class Base{
public:
Base(int x){};
};
class MyClass: public Base{
using Base::Base; // Inherit constructors from Base
}
Explicit and Implicit Constructors#
Constructors declared with the explicit
keyword can only be used for direct initialization, preventing the constructor from being accidentally called during implicit conversions:
class MyClass {
public:
explicit MyClass(int a) {}
};