C++ Constructor Notes

  1. Anonymous Objects

First, you should make it clear that the anonymous object is one that has no object name and destructs when the constructor is called. The following is illustrated by the constructor and destructor of the code capture class:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "Calls to parametric constructors" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "Call to copy constructor" << endl;
    } 
    ~Solution(){
        cout << "Call to destructor" << endl;
    }
private:
    int m_num1;
    int m_num2;
};

int main()
{
    Solution(8,9); // Solution(8,9) Anonymous object
    
    system("pause");
    return 0;
}

The result of running the code is:

 

 

As you can see from the results of the code run, when an anonymous object is created, the class's constructor is called, and the destructor is immediately called. We can initialize the members of the initialization class directly using anonymous objects with the following code:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "Calls to parametric constructors" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "Call to copy constructor" << endl;
    } 
    ~Solution(){
        cout << "Call to destructor" << endl;
    }

    int m_num1;
    int m_num2;
};

int main()
{
    Solution s1(Solution(8,9));
    // Solution s1 = Solution(8,9); //Explicit Solution(8,9) Anonymous Object Initialization Class Members
    // Solution s1 = {8,9};        //{8,9}Equivalent to Solution(8,9)
    cout << "s1.m_num1 = " << s1.m_num1 << endl;
    cout << "s1.m_num2 = " << s1.m_num2 << endl;   
    system("pause");
    return 0;
}

The results are as follows:

The code calls the constructor once, so it is visible that anonymous objects can initialize class members by converting anonymous objects into s1 objects, which need to be distinguished from copy constructors:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "Calls to parametric constructors" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "Call to copy constructor" << endl;
    } 
    ~Solution(){
        cout << "Call to destructor" << endl;
    }

    int m_num1;
    int m_num2;
};

int main()
{
    Solution s1(10,11);
    Solution s2(s1);
    cout << "s2.m_num1 = " << s2.m_num1 << endl;
    cout << "s2.m_num2 = " << s2.m_num2 << endl;
    system("pause");
    return 0;
}

The result of running the code is:

 

By comparing the above two codes, you can see that initializing class members through anonymous objects is not a copy construct, but a replacement. Initializing class members through anonymous objects does not call a copy constructor.

2. Time to invoke copy constructor

This autumn's quiz examines the timing of constructor invocation, which is usually combined with inheritance and polymorphism. Here we first explain when to invoke the copy constructor, and then explain in detail when to invoke the constructor with inheritance and polymorphism.

1. Initialize a new object using an object that has already been created, such as the code above. A parametric constructor is called when the object s1 is created, and a copy construct is called when s1 is used to initialize s2.

2. When a parameter of a function is an object that requires value transfer

3. When a function returns an object

The following code validates when a parameter to a function is an object that requires value passing:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "Calls to parametric constructors" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "Call to copy constructor" << endl;
    } 
    ~Solution(){
        cout << "Call to destructor" << endl;
    }
public:
    int m_num1;
    int m_num2;
};

void showClassNum(Solution s) {
    cout << s.m_num1 << endl;
    cout << s.m_num2 << endl;
}

int main()
{
    Solution s1(10,11);
    showClassNum(s1);
    system("pause");
    return 0;
}

The result of running the code is:

 

  As you can see from the result of running the code, the s1 object calls the parametric constructor, the copy constructor is called when s1 is passed to the function's parameter s, and the function executes the destructor that calls the s object.

When the return value of a function is an object, the copy constructor is also called:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "Calls to parametric constructors" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "Call to copy constructor" << endl;
    } 
    ~Solution(){
        cout << "Call to destructor" << endl;
    }

    void changeNum(int a, int b) {
        m_num1 = a;
        m_num2 = b;
    }

    void showNum() {
        cout << "m_num1 = " << m_num1 << endl;
        cout << "m_num2 = " << m_num2 << endl;
    }
public:
    int m_num1;
    int m_num2;
};

Solution clearClassNum(Solution s) {
    s.changeNum(0,0);
    return s;
}

int main()
{
    Solution s1 (10,10);
    s1 = clearClassNum (s1);
    s1.showNum();
    system("pause");
    return 0;
}

The result of running the code is:

As you can see from the result of running the code, copy construction is called when a function passes parameters, and then when the function returns an object, copy construction is also called.

It is important to note that when a function returns an object, it should not be returned by reference because variables within the function are stored in the stack area, which frees up memory after the function is executed, and the reference will be problematic.

3. Deep and Shallow Copies

The questions we like to ask during the interview are: first, the shallow copy is our common copy, which implements the copy of the object members. The deep copy is to apply for space in the heap area, and then do the copy operation. The shallow copy can be done by the compiler, but the deep copy needs to be done by ourselves. If we have memory opened up in the heap area, we must provide the copy constructor by ourselves. Prevent duplicate memory release problems caused by shallow copies. Code validation is as follows:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), pm_num2(new int(b)) {
        cout << "Calls to parametric constructors" << endl;
    };
      //If you do not create new memory in the heap using a deep copy, it can cause duplicate heap release problems caused by a shallow copy,Cause program error
    Solution(const Solution& s):m_num1(s.m_num1), pm_num2(new int(*s.pm_num2)) {
        cout << "Call to copy constructor" << endl;
    }
    ~Solution(){
        cout << "Call to destructor,Release memory for heap area requests" << endl;
        if (pm_num2 != nullptr) {
            delete pm_num2;
        }
    }

    void changeNum(int a, int b) {
        m_num1 = a;
        *pm_num2 = b;
    }

    void showNum() {
        cout << "m_num1 = " << m_num1 << endl;
        cout << "m_num2 = " << *pm_num2 << endl;
    }
public:
    int m_num1;
    int* pm_num2;
};

void func()
{
    Solution s1 (10,10);
    Solution s2(s1);
    s2.showNum();
}

int main()
{

    func();
    system("pause");
    return 0;
}

The result of running the code is:

 

Tags: C++

Posted on Sun, 31 Oct 2021 14:19:01 -0400 by Naithin