C++ Basic Review

1. Constructors and Destructors
Question: What is the principle of constructors? In Test t1(10, 20), t1 is a class object, not a function name. How can we add arguments in double parentheses as directly as a function call? What is the principle of his implementation? Test1 = Test (10, 20); the constructor does not return a value, how can it be used as a right value?

    class  Test
    {
    public:
        Test(int x, int y)
        {
            m_x = x;
            m_y = y;
        }
    private:
        int m_x;
        int m_y; 
    };
    main::
    Test t1(10, 20);
    Test t1 = Test(10, 20);/*Generates an anonymous class object, converts the anonymous class object to a t1 variable, and does not call the equal sign operator for assignment.*/

2. Default constructors and default destructors
If the display defines a constructor and destructor, the default constructor and destructor are overridden

There will be a default parametric constructor, a default copy constructor, and a default equality operator in the class.
1. When there is no explicit parametric constructor (explicit parametric constructor, explicit parametric constructor, explicit copy constructor), the default parametric constructor will be called.
2. When the copy constructor ** is not displayed, the default copy constructor is called
 3. When no destructor ** is displayed, the default destructor is called

3. Copy constructor
The compiler will have a default copy constructor
Simply copy the member variables of another object to yourself

    void operator=(const Test& another)
    {
        m_x = another.m_x;
        m_y = another.m_y;
    }
    Test(const Test& another)
    {
        m_x = another.m_x;
        m_y = another.m_y;
    }
    main::
    //Call a constructor with parameters
    Test t2(10, 20);
    t2.printTest();
    //If you don't write Test (const Test & other), the system automatically adds this constructor, which is the default copy constructor.
    //What you do is copy all the member variables of another object to your own member variables.
    Test t3(t2);
    t3.printTest();

    //Two methods of calling copy constructors are equivalent
    Test t4 = t2;
    t4.printTest();
    //--------------------- separator----------------------------------------------------------------------------------------------------------
    //This is a parameter-free constructor called when t5 is defined
    Test t5;
    //Equal sign operator overloading, which is an assignment operation  
    t5 = t1;

    //Note that copy constructors cause deep and shallow copy problems

4. Call order of constructors (six scenarios) (emphasis)
1).

        void test1()
        {
            //Constructor: Who creates first, who calls first
            Test t1(10, 20);
            Test t2(t1);//Test t2 = t1;
            //Destructor: Who creates it first and then calls it
            //The principle is that the stack goes into and out of the stack. After the object is defined, the stack is pressed, and when it is released, it goes out of the stack.
        }
2).
        void func(Test t)//Test t = t1; //Test's copy constructor
        {
            cout << "func begin..." << endl;
            t.printT();
            cout << "func end..." << endl;
        }

        void test3()
        {
            cout << "test3 begin..." << endl;
            Test t1(10, 20);

            func(t1);

            cout << "test3 end..." << endl;
        }

        //test3 begin...
        //Test(int x, int y)...
        //Test(const Test &)... //When calling fun, it is equivalent to Test t = t1, calling the copy constructor of T
        //func begin...
        //x = 10, m_y = 20      //Call t.printT();
        //func end...   //cout << "func end..." << endl;
        //~Test()...   //Calling the destructor of t
        //test3 end...  //cout << "test3 end..." << endl;
        //~Test()...   //Call the destructor of t1
3).
Test func2(){
    cout << "func2 begin..." << endl;
    Test temp(10, 20);
    temp.printT();

    cout << "func2 end..." << endl;

    return temp;//This step calls the copy constructor, the anonymous object = temp anonymous object. copy construct (temp)
}//_The destructor will not be called until the temporary variable temp reaches this location.

void test4(){
    cout << "test4 being.. " << endl;
    func2();/*Returns an anonymous object. When a function returns an anonymous object, there is no variable outside the function to receive it. The anonymous object will no longer be used, and the compiler will directly take the anonymous object.*/
        //Recycle instead of waiting for the rectification function to execute.
        //Anonymous objects are recycled.

    cout << "test4 end" << endl;
}


    test4 being..
    func2 begin...
    Test(int x, int y)...
    x = 10, m_y = 20
    func2 end...
    Test(const Test &)...
    ~Test()...//First deconstruct temp
    ~Test()...//Because there is no variable acceptance, the compiler destructs the temporary variable when func2() is called.
    test4 end
4).
Test& func2(){
    cout << "func2 begin..." << endl;
    Test temp(10, 20);
    temp.printT();
    cout << "func2 end..." << endl;
    return temp;
}
void test4(){
    cout << "test4 being.. " << endl;
    Test t1 = func2();//Returns a temp reference
    t1.printT();
    cout << "test4 end" << endl;
}

/*
    test4 being..
    func2 begin...
    Test(int x, int y)...
    x = 10, m_y = 20
    func2 end...
    ~Test()...                         //Destruction of temp
    Test(const Test &)...               //Copy construction, Test t1 = func2(); returns a temp reference
    x = -858993460, m_y = -858993460
    test4 end
    ~Test()...
*/
5).
Test func2(){
    cout << "func2 begin..." << endl;
    Test temp(10, 20);
    temp.printT();
    cout << "func2 end..." << endl;
    return temp;
}//Anonymous object = temp anonymous object. copy construction (temp)

void test5(){
    cout << "test 5begin.. " << endl;
    Test t1 = func2(); 
    //Will T1 copy construct be triggered to t1. copy (anonymous)?
    //Instead of triggering a copy of t1, the anonymous object is corrected to t1.
    //Name this anonymous object t1.
    cout << "test 5 end.." << endl;
}

/*
    test 5begin.. 
    func2 begin...
    Test(int x, int y)...
    x = 10, m_y = 20
    func2 end...
    Test(const Test &)...
    ~Test()...
    test 5 end..
    ~Test()...
*/
6) scenario 6
Test func2(){
    cout << "func2 begin..." << endl;
    Test temp(10, 20);
    temp.printT();
    return temp;
}
void test6(){
    cout << "test6 begin..." << endl;
    Test t1; //t1 has been initialized.
    t1 = func2();
    t1.printT();
    cout << "test6 end.." << endl;
}
/*
    test6 begin...
    Test()...
    func2 begin...
    Test(int x, int y)...
    x = 10, m_y = 20
    func2 end...
    Test(const Test &)...
    ~Test()...
    Test(const Test &)...
    ~Test()...             //Anonymous objects are released after assignment
    x = 10, m_y = 20
    test6 end..
    ~Test()...
*/

5. Deep copy and shallow copy
// Trigger timing: The default copy constructor or the default equality operator in the class is called
1. Shallow copy example

class A
{
private:
    int _ma;
    int _mb;
    char *_mch;
public:
    A(int a, int b, char *ch)
    {
        cout << "A(int a, int b, char *ch)" << endl;
        _ma = a;
        _mb = b;
        int len = strlen(ch);
        _mch = (char *)malloc(sizeof(char)*(len + 1));
        strcpy(_mch, ch);
    }
    //A(A &_newa)
    //{
    //}
    ~A()
    {
        cout << "~A()" << endl;
        if (_mch != NULL)
        {
            free(_mch);
            _mch = NULL;
        }
    }
};
void fun1()
{
    A a1(10, 20, "lily");
    A a2(a1);
}
//When a1 is defined, the constructor with parameters is called, block memory space is allocated in the heap area, and the content of the text constant area is copied to that area.
//The default copy constructor is called when A2 is defined. The main logic of the default copy constructor is to copy everything in a1 memory to a2.
//Although this approach does not have much effect on normal parameters, it hides an error for pointer variables.

//When the default copy constructor copies all the contents of A1 to a2, a2._mch also points to the memory area that a1._mch points to.
//At the end of the fun1 function, A2 calls the destructor to release the memory area pointed to by a2._mch
//Then A1 calls the destructor to release the memory area pointed to by a1._mch again, so that the program will report an error if the same memory area is released twice.
//To solve this problem, we can use a deep copy to define a copy constructor in the class and create a memory area in the heap.

A(A &_newa)
{
    _ma = _newa._ma;
    _mb = _newa._mb;
    int len = strlen(_newa._mch);
    _mch = (char *)malloc(sizeof(char)*(len + 1));
    strcpy(_mch, _newa._mch);
}
//So the two pointers of the two objects point to two different memory regions, respectively.

6. Constructor initialization list
1. Objects that contain another class in a class object
2. If the class object contains two or more classes of objects, the initialization order of member objects in the initialization list of constructors

class A
{
public:
    A(int a)
    {
        cout << "A()..." << a << endl;
        m_a = a;
    }
    ~A() 
    {
        cout << "~A()" << endl;
    }
    void printA() 
    {
        cout << "a = " << m_a << endl;
    }
private:
    int m_a;
};
class B
{
public:
    B(A &a1, A &a2, int b) :m_a1(a1), m_a2(a2)//Call the default copy constructor
    {
        //If there is no constructor parameter list
        //This defines an object of class B that contains two classes A. When creating the object of class B, the problem arises when initializing the member variable m_a1 and m_a2.
        //1. Can't directly m_a1 = a1. This is an assignment by calling the equality operator of class A.
        //2. It is not possible to treat m_a1(a1) as a function. Of course, it is wrong to treat m_a1 as a function.
        //So two member variables m_a1 and m_a2 are initialized in the parameter list.

        cout << "B(A &a1, A &a2, int b)" << endl;
        m_b = b;
    }
//The order in which object members are constructed is independent of the order in which the list is initialized.
//Relating to the order in which member variables are defined
//Definitions are initialized first
    B(int a1, int a2, int b) : m_a1(a1), m_a2(a2)
    //  B(int a1, int a2, int b) : m_a2(a2), m_a1(a1)
    {
        cout << "B(int, int, int)..." << endl;

        m_b = b;
    }
    void printB() 
    {
        cout << "b = " << m_b << endl;
        m_a1.printA();
        m_a2.printA();
    }
    ~B()
    {
        cout << "~B().." << endl;
    }
private:
    int m_b;
    A m_a1;
    A m_a2;
};
void test1()
{
    A a1(10), a2(100);
    B b(a1, a2, 1000);

    b.printB();
}

7.new and delete keywords

1).new and delete keywords have the same points as malloc and free
    Like malloc, new allocates a block of memory in the heap and returns the address of that memory in the heap.
    delete, like free, frees the specified memory in the heap

    new space can be free d up
    The space created by malloc can be released by delete

2. Differences between new and delete keywords and malloc and free
    A).malloc and free are functions defined in the standard library stdlib.h. Since they are functions, there are Stack-in and stack-out operations when they are invoked.
       The new and delete keywords are C++ operators. Like sizeof in root C, there is no Stack-in and stack-out operation when a function is called.
    B). In the case of class A in 6
        A *temp = (A*)malloc(sizeof(A));
        // At this point malloc does not initialize the member variables, and if temp.printA() is called at this time, the output is scrambled.
        A* temp = new A(10);
        // The new keyword can create an A space in the heap and call the constructor of A to initialize the space and return the address of the space.
    C).new and delete trigger class constructors and destructors
class Dog
{
public:
    Dog(int id,char *name)
    {
        cout << "Dog(int id)" << endl;
        m_id = id;
        int len = strlen(name);
        m_name = (char *)malloc(sizeof(char)*(len+1));
        strcpy(m_name,name);
    }
    ~Dog()
    {
        cout << "~Dog()" << endl;
        if(m_name != NULL)
        {
            free(m_name);
            m_name = NULL;
        }
    }
    void showDog()
    {
        cout << "showDog()" << endl;
    }
private:
    int m_id;
    char *m_name;
};
void fun1()
{
    Dog *d1 = new Dog(10,"SUNNY");
    d1->showDog();

    if (d1 != NULL)
    {
        delete d1;
    }

}
/*
    Dog(int id)
    showDog()
    ~Dog()
*/

//In the heap, an object space of a class is created with int and char* members - > space A
//Another m_name space is created in the constructor to store the name of the Dog - --> Space B
//Call delete d1; when delete is responsible for releasing "space A", it triggers the class destructor, and the destructor is responsible for releasing "space B"     
//The destructor is not responsible for releasing the object itself, but for releasing the extra memory space created by the object on the heap.

8.static member variables

1. Initialization method
    static member variables must be initialized outside the class
    Type class name: static member variable = value
class Box
{
private:
    int len;
    int width;          
public:
    static int high;
    Box(int l, int w) 
    {
        len = l;
        width = w;
    }
    void volume()
    {
        cout << "high = " << high << endl;
        cout << "volume = " << len*width*high << endl;
    }           
};

    int Box::high = 10;//static member variables must be initialized outside the class
2).stay public area
    Box b1(10, 20);
    b1.volume();

    Box::high = 20;//static member variables are placed in the public area and can be accessed outside the class for modification
    b1.volume();
3).stay private area
class Box2
{
private:
    int len;
    int width;
    static int high;
public:
    Box2(int l, int w)
    {
        len = l;
        width = w;
    }
    void volume()
    {
        cout << "high = " << high << endl;
        cout << "volume = " << len*width*high << endl;
    }
    static void setHigh(int h)//If the static member variable is placed in the private area, it must be modified through the static member function when it is modified.
    {
        high = h;
    }
};


        Box2 b2(10, 20);
        b2.volume();

        Box2::setHigh(20);//Because it is static, it can be accessed by adding::

        b2.volume();
4).static Out-of-class storage of members,Class size,Not included
cout << "sizeof(Goods) = " << sizeof(Goods) << endl;
//8 bytes, int len; int width;
//static int high; in the data area, not included in the class

Posted by micknic on Tue, 16 Apr 2019 13:57:33 -0700