This blog was last modified 412 days before.
Type of Class Constructor
There are 4 general type of constructor.
- Constructor
- Default Constructor
- Copy Constructor
- Delegate Constructor
- Move Constructor
Default Constructor
Default Constructor is a class constructor has no parameter.
You could explicitly declare a default constructor for a class like below:
class CustomClass
{
public:
CustomClass() // default constructor
{
// do something here
}
}
Compiler will automatically generate a default constructor if all following condition met:
- No any other constructor.
- No explicit
delete
of the default constructor.
Prevent Default Constructor Auto-generate
If you want to explicitly tell compiler you don't need a default constructor, you can use delete
keyword.
CustomClass() = delete;
Then when you try to create a new instance by calling the default constructor, you will receive compliant by compiler.
CustomClass instance; // Error: the default constructor of "CustomClass" cannot be referenced -- it is a deleted function
Copy Constructor
Copy Constructor will be called when you try to create a new instance of this class based on the data of an existing instance.
When will be called
More detailed, copy constructor will be called in following cases:
- Creating new instance from existing one.
- Passing class instance as a function parameter by value.
- Return a class instance. (Not really in morden compiler, we will talk more about this in the move constructor part)
CustomClass a; // default constructor called
CustomClass b(a); // copy constructor called
CustomClass c = a; // copy constructor called
void some_func(CustomClass ins);
some_func(c); // copy constructor called
Move Constructor
To be simple, move constructor will be called when construct a new instance from an existing rValue (temporary) instance.
If you don't know what is lValue and rValue , then you need to figure it out first before continue learning about move constructor.
Check example below:
class CustomClass;
CustomClass a;
CustomClass b = std::move(a); // move constructor called
The example is simple, std::move(a)
turns a into a rValue .
RVO (Return Value Optimization)
Morden C++ compiler has done a great work to try to reduce the times we call copy constructor.
CustomClass some_func(CustomClass ins) // ins1 in main has been copyed to local variable ins here
{
ins.id++;
CustomClass tmpIns = ins; // copy constructor called: ins -> tmpIns
tmpIns.id++;
return tmpIns; // copy/move assignment operator called: tmpIns -> ins2 (in main),
// you can simply consider this equals ins2(in main) = tmpIns (in some_func)
}
int main()
{
// CustomClass instance;
CustomClass ins1(1); // constructor called
CustomClass ins2; // default constructor called
ins2 = some_func(ins1); // copy constructor called: ins1 -> ins(local in some_func)
cout << ins2.id;
return 0;
}
Output of the code above is:
Constructor Called
Default Constructor Called
Copy Constructor Called: existId: 1
Copy Constructor Called: existId: 2
Copy/Move Assignment Operator Called: existId: 3
3
existId
here means theid
of the instance that being copied.id
is aint
type member variable of this class.
The last line:
Copy/Move Assignment Operator Called: existId: 3
This means if this class has an move assignment operator, then move assign will be used, otherwise, copy assignment will be used.
Let's see another example:
CustomClass some_func(CustomClass ins)
{
ins.id++;
CustomClass tmp = CustomClass(ins); // Copy Constructor Called: ins->tmp
CustomClass tmp2 = tmp; // Copy Constructor Called: tmp->tmp2
return tmp2; // do nothing, since tmp2 actually use the addr of ins3
}
int main()
{
// CustomClass instance;
CustomClass ins1(1); // Constructor Called
CustomClass ins3 = some_func(ins1); // Copy Constructor: ins1 -> ins (local in some_func)
return 0;
}
Output:
Constructor Called
Copy Constructor Called: existId: 1
Copy Constructor Called: existId: 2
Copy Constructor Called: existId: 2
So based on above experiment we know, morden C++ compiler will do more than we expected.
Receiving Variable Created Before Function Return
If the receiving variable (for example ins2 = some_func(ins1);
, then ins2
is the receiveing variable here) has been created already when function return, then function will try to call move assignment operator first, if no such operator, then compiler will try calling copy assignment operator .
Receiving Variable Not Created Before Function Return
If the receiving variable not been created, then the return value and the receiving value is actually a same instance! You don't even need to move them. For example in second example, tmp2
created in some_func()
is actually use the same address of ins3
in main()
function.
In this situation, the allocation of memory is done by original calling function (In the second example, that is main()
is responsible to find a place in memory which will be used to store ins3
in main, a.k.a.: tmp2
in some_func()
, they are the same thing in this example). However main()
is not responsible to initialize this memory, some_func()
is responsible to initialize a new CustomClass
instance on this memory. Check the diagram below:
If you are using TSU Press - C++ Programming as your textbook, you can check out P145 for more info.
No comment