C++ core programming classes and objects --- object characteristics --- constructors and destructors (initialization and cleanup of objects)

Table of contents

1. Constructor and destructor (initialization and cleanup of objects)

effect

Function realization

2. Constructor

Syntax: classname() {}

3. Destructor

Syntax ~classname(){}

4. Classification and calling of constructors

5. Timing of calling copy constructor

6. Constructor calling rules

7. The problem of deep copy and shallow copy        

8. Initialization list

9. Class objects as class members

10. Static members

1. Static member classification

2. Static member variables

3. Static member functions


1. Constructor and destructor (initialization and cleanup of objects)

Object initialization and cleanup

        In life, the electronic products we buy basically have factory-installed covers. On a certain day, when we are not using them, we will also delete some of our own information and data to ensure safety.

        Object-oriented in C++ comes from life. Each object will also have initial settings and settings for cleaning up data before the object is destroyed.

effect

        An object or variable has no initial state, and the consequences of its use are unknown.

        Similarly, failure to clean up an object or variable in time after using it will also cause certain security issues.

Function realization

        C++ uses constructors and destructors to solve the above problems. These two functions will be automatically called by the compiler to complete object initialization and cleanup. Initialization and cleanup of objects are things that the compiler forces us to do, so if we don’t Provide construction and destruction, the compiler will provide the constructor and destructor provided by the compiler are empty implementations.

        

  • Constructor: Mainly used to assign values ​​to the member properties of the object when creating an object. The constructor is automatically called by the compiler and does not need to be called manually.
  • Destructor: Mainly used to automatically call the system before the object is destroyed to perform some cleanup work.

 

2. Constructor

Syntax: classname() {}

  • Constructor has no return value and does not write void
  • The function name is the same as the class name
  • Constructors can have parameters, so overloading can occur
  • When the program calls the object, it will automatically call the constructor. There is no need to call it manually, and it will only be called once.

Example:

#include<iostream>

using namespace std;

class person{

public:// 在公共作用域下在主函数才能访问到

    // 构造函数

person()

    {

        // 在创建一个对象的时候会自动调用一次

        cout<<"person 构造函数的调用"<<endl;

    }

};

int main ()

{

    person p;

    return 0;

}

operation result:

 

3. Destructor

Syntax ~classname(){}

  • Destructor has no return value and does not write void
  • The function name is the same as the class name
  • The destructor cannot have parameters, so overloading cannot occur.
  • The program will automatically call destructor before the object is destroyed. There is no need to call it manually, and it will only be called once.

Example:

#include<iostream>

using namespace std;

class person{

public:// 在公共作用域下在主函数才能访问到

    // 析构函数

    ~person ()

    {

        cout<<"析构函数的调用"<<endl;

    }

};

void test()

{

    person p; // 在栈上的数据,在test执行完毕后就会释放这个对象

}

int main ()

{

    person p1;  // 在main函数中也会在整个程序中执行完的时候执行

    return 0;

}

operation result:

4. Classification and calling of constructors

Two classification methods:

  •         Divided according to parameters: parameterized construction and parameterless construction
  •         Divided by type: ordinary construction and copy construction

Three calling methods:

  •         bracketing
    •         display method
      •         Implicit conversion method

Example:

#include<iostream>

using namespace std;

class person {

public:

    // 构造函数

    person() // 无参

    {

        cout << "person 的无参数的构造函数调用。" << endl;

    }

    person(int a) // 有参

    {

        age = a;

        cout << "person 的有参数的构造函数调用。" << endl;

    }



    // 拷贝构造函数

    person(const person& p)

    {

        // 把另一个person的属性传入进来 +const 防止改变原来的属性

        // 将传入的人身上的所有的属性全部拷贝到自己的身上

        age = p.age;

        cout << "person 的拷贝构造函数调用。" << endl;

    }



    // 析构函数

    ~person()

    {

        cout << "person 的析构函数的调用。" << endl;

    }

    int age;

};

void test()

{

    // 调用

    // 1. 括号法

    cout << "括号法调用" << endl;

    person p1; // 默认构造函数调用

    // 注意事项1:默认构造函数的调用不要加()  因为会被认为是一个函数的声明,不会认为在创建对象

    person p2(10); // 调用有参构造函数

    // 拷贝构造函数的调用

    person p3(p2); // 拷贝构造函数的调用



    cout << "p2的年龄:" << p2.age << endl;

    cout << "p3的年龄:" << p3.age << endl;

    cout << endl;



    // 2. 显示法

    cout << "显示法调用" << endl;

    person p4; // 默认构造函数

    person p5 = person(10); // 有参构造

    person p6 = person(p3);  // 拷贝构造

    // 右侧相当于匿名对象,当执行结束后系统会立即回收掉匿名对象
    // 注意事项2:不要利用拷贝构造函数 初始化 匿名对象
    // 编译器会认为  person(p3)  === person p3,相当于时对象的声明

    cout << endl;



    cout << "测试匿名对象" << endl;

    person(10);

    cout << "aaa" << endl << endl;



    // 3. 隐式转换法

    cout << "隐式转换法调用" << endl;

    person p7 = 10; // 相当于person p3 = person(10);是一种有参构造

    person p8 = p7;

    cout << endl;

   }

int main()

{

    test();

    return 0;

    system("pause");

}

operation result:

5. Timing of calling copy constructor

There are usually three situations when the copy constructor is called in C++:

  • Initialize a new object using an already created object
  • Pass values ​​to function parameters using value passing method
  • Returns a local object by value

Example:

#include<iostream>
using namespace std;
// 拷贝函数的三种调用时机
class person {
public:
    person()
    {
        cout << "person 的默认函数构造" << endl;
    }
    // 有参
    person(int in_age)
    {
        age = in_age;
        cout << "有参构造函数" << endl;
    }
    // 拷贝
    person(const person& p)
    {
        age = p.age;
        cout << "拷贝构造函数" << endl;
    }
    // 析构
    ~person()
    {
        cout << "person 的析构函数的调用" << endl;
    }
//private:
    int age;
};

// 1. 使用一个已经创建完毕的对象来初始化一个新的对象
void test01()
{
    person p1(20); // 有参构造
    person p2(p1); // 拷贝构造
    cout << "p2的年龄为:" << p2.age << endl;
}

// 2.值传递的方式给函数参数传值
void do_work(person p)
{

}

void test02()
{
    person p;
    do_work(p);
}

// 3. 以值的方式返回局部对象
person do_work2()
{
    person p1;
    cout << (int*)&p1 << endl;
    return p1;  // 拷贝一个新的对象用于返回
}

void test03()
{
    person p = do_work2(); // 得到的是拷贝的返回值
    cout << (int*)&p << endl;
}

int main()
{
    test01();
    cout << endl;
    test02();
    cout << endl;
    test03();
    return 0;
}

operation result:

6. Constructor calling rules

  • By default, the C++ compiler adds at least 3 functions to a class
  •         1. Default constructor (no parameters, empty function body)
  •         2. Default destructor (no parameters, empty function body)
  •         3. Default copy constructor to copy the value of attributes
  • Constructor calling rules:
  •         If you write a parameterized constructor, the compiler no longer provides a default constructor, but it still provides a copy constructor.
  •         If a copy constructor is written, the compiler will no longer provide a default constructor and a parameterized constructor.

Example:

#include<iostream>

using namespace std;

class person

{

public:

    // 默认

    person()

    {

        cout<<"person 的默认函数构造"<<endl;

    }

 

    // 有参

    //  如果自己提供了有参构造,编译器就不再提供默认构造

    person(int in_age)

    {

        age = in_age;

        cout<<"person 的有参构造函数"<<endl;

    }

 

    // 拷贝

    // 如果自己不提供,编译器也会提供拷贝函数,值拷贝(两个析构,一个有参)

    /*person(const person& p)

    {

        this->age = p.age;

        cout<<"person 的拷贝构造函数"<<endl;

    }*/

 

    // 析构

    ~person()

    {

        cout<<"person 的析构函数的调用"<<endl;

    }

 

    int age;

};

void test01()

{

    person p;

    p.age = 18;

 

    person p1(p);

    cout<<"p1的年龄:"<<p1.age<<endl;

}

int main()

{

    test01();

    return 0;

}

operation result:

 

7. The problem of deep copy and shallow copy        

  • Shallow copy: simple assignment copy operation
  • Deep copy: Re-apply space in the heap area and perform copy operations

The problem caused by shallow copy is the repeated release of memory in the heap area, which can be solved by using deep copy.

Example:

#include<iostream>

using namespace std;

class person

{

public:

    // 默认

    person()

    {

        cout<<"person 的默认函数构造"<<endl;

    }

 

    // 有参

    person(int age, double height)

    {

        this->age = age;

        cout<<"person 的有参构造函数"<<endl; 

        this->height = new double(height); // new 一个新的指针变量,创建在堆区

    }

 

    // 拷贝

    person(const person & p)

    {

        this->age = p.age;  // 编译器默认实现

        cout<<"person 的拷贝构造函数"<<endl;

        // this->age = p.height; // 编译器的 拷贝构造默认实现方式

 

        // 解决浅拷贝带来的问题

 

        // 两个对象的指针分别设立在不同的存储地址

        this->height = new double(*p.height); // 利用深拷贝来解决浅拷贝的问题

    }

 

    // 析构

    ~person()

    {

        // 将我们在堆区开辟的数据做释放操作

        if(height !=NULL){

            delete height;

            height = NULL;

        }

        cout<<"person 的析构函数的调用"<<endl;

    }

   

    int age;

    double * height; // 身高数据的地址

 

    // 编译器提供的拷贝构造函数 会做浅拷贝处理  会重复释放

    // 身高是定义在堆区的,指针类型的存储的是地址,不能重复释放

};

void test01()

{

    person p1(18,1.80);

    cout<<"p1的年龄为: "<<p1.age<<endl;

    cout<<"p1的身高为:"<<*p1.height<<endl;

 

    person p2(p1);

    cout<<"p2的年龄为:"<<p2.age<<endl;

    cout<<"p2的身高为:"<<*p2.height<<endl;

}

int main()

{

    test01();

    return 0;

}

operation result:

 

8. Initialization list

Function: C++ provides initialization list syntax for initializing properties.

Syntax: Constructor(): Property 1 (value 1), Property 2 (value 2)... {}

Example:

#include<iostream>

using namespace std;

class person

{

public:

    // 传统初始化操作

    /*person(int a ,int b,int c)

    {

        A = a;

        B = b;

        C = c;

    }*/

 

    // 初始化列表属性,赋值默认值

    /*person():A(10),B(20),C(30)

    {

 

    }*/

 

    // 设置成变量

    person(int a,int b,int c):A(a),B(b),C(c)

    {

 

    }

 

    int A;

    int B;

    int C;

};

void test01()

{

    // 每次调用一种

    person p(10,20,30);

    cout<<"A = "<<p.A<<endl;

    cout<<"B = "<<p.B<<endl;

    cout<<"C = "<<p.C<<endl;

 

}

int main()

{

    test01();

    return 0;

}

operation result: 

9. Class objects as class members

A member in a C++ class is an object of another class, which is called an object member.

For example:

class A{};

class B

{

A a;

};

Class B has object A as a member, and A is an object member

Then, when creating the B object, the construction and destruction order of A and B is ABBA

Example:

#include<iostream>

#include<string>

using namespace std;

// 设计一个手机类

class phone{

public:

    // 给手机命名

    // 构造函数

    phone(string in_name)

    {

        p_name = in_name;

        cout<<"这是phone的构造函数调用"<<endl;

    }

    ~phone()

    {

            cout<<"phone 析构函数的调用"<<endl;

    }

 

    // 手机类型的设计

    string p_name;

};

// 设计一个人的类

class person

{

public:

    // 获取内容

    person(string name,string p_name):my_name(name),my_phone(p_name)

    {

        // 相当于下边这样,隐式转换法

        // phone my_phone = p_name;

        cout<<"这是person 的构造函数调用"<<endl;

    }

    ~person()

    {

            cout<<"person 析构函数的调用"<<endl;

    }

 

// 姓名

     string my_name;

    // 手机

    phone my_phone;

};

// 当其他类作为本类的成员,构造时先构造其他类的对象,再构造自身

//  析构时先析构自身,再析构其他的对象

void test01()

{

    person p("张三","苹果MAX");

    cout<<p.my_name<<"有一个"<<p.my_phone.p_name<<"手机"<<endl;

}

int main()

{

    test01();

    return 0;

}

operation result:

10. Static members

        Static members are called static members by adding the keyword static before member variables and member functions.

1. Static member classification

Static member variables:    

  • All objects share the same data
  • Allocate memory during compilation phase
  • Declaration within class, initialization outside class

Static member function:

  • All objects share the same function
  • Static member functions can only access static member variables

2. Static member variables

Example:

#include<iostream>

#include<string>

using namespace std;

class person {

public:

    static int m_A; // 静态成员变量

    // 类内声明,类外初始化



private:

    static int m_B;// 静态成员变量

    // 在类外访问不到

};

int person::m_A = 0;

int person::m_B = 200;



// 两种访问方式

void test01()

{

    // 1. 通过对象访问

    cout << "通过对象访问" << endl;

    person p1;

    cout << "p1_a = " << p1.m_A << endl;



    person p2;
    
    p2.m_A = 200;

    cout << "共享数据,p2修改了数据,p1访问就变为p2修改后内容" << endl;

    cout << "p1_a = " << p1.m_A << endl;

    cout << "p2_a = " << p2.m_A << endl;



    // 2. 通过类名访问

    cout << "通过类名访问:" << person::m_A << endl;



    //并且私有权限类外访问不到

}

int main()

{

    test01();



    return 0;

}

operation result:

  

3. Static member functions

Example:

#include<iostream>

#include<string>

using namespace std;

class person{

public:

    // 静态的成员函数

    static void func()

    {

        m_A = 100;

        // m_B = 200;  // 静态成员函数不能访问非静态成员变量

        // 静态成员函数是每个对象都共享的,调用非静态成员变量时不知道修改的是哪个对象的变量

        cout<<"静态函数的调用"<<endl;

    }

    static int m_A; // 静态成员变量

    // 类内声明,类外初始化

    int m_B;

private:

    static  void  func2()

    {

        cout<<"这是private下的调用"<<endl;
    }

};

int person::m_A = 0;

 

// 两种访问方式

void test01()

{

    // 1. 通过对象访问

    cout << "通过对象访问" << endl;

    person p1;

    p1.func();

 

    // 2. 通过类名访问

    cout<<"通过类名访问:";

    person::func();

 

    person::func2();// 类外访问不到私有静态成员函数

}

int main()

{

    test01();
    return 0;

}

operation result: 

 

Guess you like

Origin blog.csdn.net/hjl011006/article/details/133550565