[C++] Initialization and cleanup of objects

  • The electronic products we buy in our lives basically have factory settings, and some of our own information and data will be deleted when we don’t use them on a certain day to ensure safety
  • Object-oriented in C++ comes from life, and each object will also have initial settings and settings for cleaning up data before the object is destroyed.

1. Constructor and destructor

Object initialization and cleanup are also two very important security issues

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

Similarly, after using an object or variable, if it is not cleaned up in time, it will also cause certain security problems

 

C++ uses the constructor and destructor to solve the above problems, these two functions will be automatically called by the compiler to complete the object initialization and cleanup work.

The initialization and cleanup of objects is what the compiler forces us to do, so if we do not provide construction and destruction, the compiler will provide

The constructor and destructor provided by the compiler are empty implementations.

 

  • Constructor: The main function is to assign values ​​to the member properties of the object when the object is created. The constructor is automatically invoked by the compiler without manual invocation.

  • Destructor: The main function is to automatically call the system before the object is destroyed to perform some cleanup work.

The order of execution of the destructor is strictly opposite to that of the constructor

 

Constructor syntax:类名(){}

  1. Constructor, no return value nor write void

  2. The function name is the same as the class name

  3. The constructor can have parameters, so overloading can occur

  4. The program will automatically call the structure when calling the object, there is no need to call it manually, and it will only be called once

 

Destructor syntax: ~类名(){}

  1. Destructor, no return value nor write void

  2. The function name is the same as the class name, with the symbol ~ in front of the name

  3. The destructor cannot have parameters, so overloading cannot occur

  4. The program will automatically call the destructor before the object is destroyed, there is no need to call it manually, and it will only be called once

#include <iostream>
using namespace std;

class Person
{
public:
	//构造函数
	Person()
	{
		cout << "Person的构造函数调用" << endl;
	}
	//析构函数
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}

};

void test01()
{
	Person p;
}

int main(void) {

	test01();

	system("pause");

	return 0;
}

2. Classification and calling of constructors

Two classification methods:

According to the parameters, it is divided into: parameterized structure and non-parameterized structure

Divided by type: ordinary structure and copy structure

class Person 
{
    public:
	    //无参(默认)构造函数
	    Person() 
        {
		    cout << "无参构造函数!" << endl;
	    }
	    //有参构造函数
	    Person(int a) 
        {
		    age = a;
		    cout << "有参构造函数!" << endl;
	    }
	    //拷贝构造函数
	    Person(const Person& p)
         {
		    age = p.age;
		    cout << "拷贝构造函数!" << endl;
	    }
	    //析构函数
	    ~Person() 
        {
	   	    cout << "析构函数!" << endl;
	    }
public:
	int age;
};

Three calling methods:

Bracketing

Display method

Implicit conversion

//2、构造函数的调用
//调用无参构造函数
void test01() 
{
	Person p; //调用无参构造函数
}

//调用有参的构造函数
void test02() 
{

	//2.1  括号法,常用
	Person p1(10);
	//注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
	//Person p2();

	//2.2 显式法
	Person p2 = Person(10); 
	Person p3 = Person(p2);
	//Person(10)单独写就是匿名对象  当前行结束之后,马上析构

	//2.3 隐式转换法
	Person p4 = 10; // Person p4 = Person(10); 
	Person p5 = p4; // Person p5 = Person(p4); 

	//注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
	//Person p5(p4);
}

test01 test result

test02 test result

 

Note 1: You cannot add parentheses when calling a parameterless constructor. If you add it, the compiler considers it to be a function declaration.
Note 2: You cannot use the copy constructor to initialize an anonymous object. The compiler considers it to be an object declaration.

3. When to call the copy constructor

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

  • Use an already created object to initialize a new object

  • Pass value to function parameter by value

  • Return local objects by value 

(1) Use a created object to initialize a new object

#include <iostream>
using namespace std;

class Person3 
{
public:
	Person3() 
	{
		cout << "无参构造函数!" << endl;
		mAge = 0;
	}
	Person3(int age) 
	{
		cout << "有参构造函数!" << endl;
		mAge = age;
	}
	Person3(const Person3& p) 
	{
		cout << "拷贝构造函数!" << endl;
		mAge = p.mAge;
	}
	//析构函数在释放内存之前调用
	~Person3() 
	{
		cout << "析构函数!" << endl;
	}
public:
	int mAge;
};

//1. 使用一个已经创建完毕的对象来初始化一个新对象
void test01() {

	Person3 man(100); //p对象已经创建完毕
	Person3 newman(man); //调用拷贝构造函数
	Person3 newman2 = man; //拷贝构造

	Person3 newman3;
	newman3 = man; //不是调用拷贝构造函数,赋值操作
}

int main(void) 
{
	test01();
	system("pause");

	return 0;
}

(2) Pass value to function parameter in the way of value passing

//2. 值传递的方式给函数参数传值
//相当于Person p1 = p;
void doWork(Person p1) {}
void test02() 
{
	Person p; //无参构造函数
	doWork(p);
}

(3) Return local objects by value

//3. 以值方式返回局部对象
Person doWork2()
{
	Person p1;
	cout << (int *)&p1 << endl;
	return p1;      //按照p1拷贝一个对象返回
}

void test03()
{
	Person p = doWork2();
	cout << (int *)&p << endl;
}

4. Constructor call rules

By default, the C++ compiler adds at least 3 functions to a class

1. Default constructor (no parameters, the function body is empty)

2. Default destructor (no parameters, the function body is empty)

3. The default copy constructor, copy the value of the attribute

 

The constructor calling rules are as follows:

  • If the user defines a parameter constructor, C++ no longer provides a default no-parameter construction, but will provide a default copy construction

  • If the user defines a copy constructor, c++ will not provide other constructors

5. Deep copy and shallow copy

Deep and shallow copy is a classic interview question and a common pit

Shallow copy: simple assignment copy operation

Deep copy: re-apply for space in the heap area for copy operation

#include <iostream>
using namespace std;

class Person
{
public:
	Person()
	{
		cout << "无参构造函数!" << endl;
	}
	Person(int age)
	{
		m_Age = age;
		cout << "有参构造函数!" << endl;
	}
	~Person()
	{
		cout << "析构函数!" << endl;
	}


	int m_Age;
};


void test05()
{
	Person p1(18);
	cout << "p1的年龄:" << p1.m_Age << endl;

	Person p2(p1);
	cout << "p2的年龄:" << p2.m_Age << endl;
}


int main(void)
{
	test05();

	system("pause");
	return 0;
}

Here will call the system default copy constructor

 

Then open up the height attribute in the heap area and modify the code as follows:

#include <iostream>
using namespace std;

class Person
{
public:
	Person()
	{
		cout << "无参构造函数!" << endl;
	}
	Person(int age,int height)
	{
		m_Age = age;
		m_Height = new int(height);
		cout << "有参构造函数!" << endl;
	}
	~Person()
	{
		if (m_Height != NULL)
		{
			delete m_Height;
			m_Height = NULL;
		}
		cout << "析构函数!" << endl;
	}


	int m_Age;
	int* m_Height;
};


void test05()
{
	Person p1(18,160);
	cout << "p1的年龄:" << p1.m_Age << "p1的身高:"<<*p1.m_Height<< endl;

	Person p2(p1);
	cout << "p2的年龄:" << p2.m_Age << "p2的身高:" << *p2.m_Height << endl;
}


int main(void)
{
	test05();

	system("pause");
	return 0;
}

The following error occurred

If you use the copy constructor provided by the compiler, a shallow copy will be made.

The m_Age attribute of p1 will be copied byte by byte to m_Age of p2

The attribute int* m_Height is the space opened up in the heap area, which is also copied byte by byte;

When the destructor is executed, p2 executes the destructor first, that is, the memory in the heap area is released; p1 will also execute the destructor, and the memory in the heap area will be released again at this time, which is an illegal operation.

This is the problem caused by shallow copy (repeated release of memory in the heap area).

Use deep copy to solve the problem of shallow copy

#include <iostream>
using namespace std;

class Person
{
public:
	Person()
	{
		cout << "无参构造函数!" << endl;
	}
	Person(int age,int height)
	{
		m_Age = age;
		m_Height = new int(height);
		cout << "有参构造函数!" << endl;
	}

	//自己实现拷贝构造函数 来解决浅拷贝的问题
	Person(const Person& p)
	{
		//编译器写的
		//m_Age = p.m_Age;
		//m_Height = p.m_Height;

		//深拷贝
		m_Age = p.m_Age;
		m_Height = new int(*p.m_Height);

		cout << "拷贝参构造函数!" << endl;
	}

	~Person()
	{
		if (m_Height != NULL)
		{
			delete m_Height;
			m_Height = NULL;
		}
		cout << "析构函数!" << endl;
	}


	int m_Age;
	int* m_Height;
};


void test05()
{
	Person p1(18,160);
	cout << "p1的年龄:" << p1.m_Age << "p1的身高:"<<*p1.m_Height<< endl;

	Person p2(p1);
	cout << "p2的年龄:" << p2.m_Age << "p2的身高:" << *p2.m_Height << endl;
}


int main(void)
{
	test05();

	system("pause");
	return 0;
}

Summary: If the attribute is developed in the heap area, you must provide your own copy constructor to prevent problems caused by shallow copy.

 

6. Initialization list

effect:

C++ provides initialization list syntax to initialize properties

grammar:构造函数():属性1(值1),属性2(值2)... {}

Traditional form

#include <iostream>
using namespace std;

class Person6
{
public:

	//传统方式
	Person6(int a, int b)
	{
		m_A = a;
		m_B = b;
	}
	int m_A;
	int m_B;
};

void test06()
{
	Person6 p(10, 20);
	cout << p.m_A << endl;
	cout << p.m_B << endl;
}



int main(void)
{
	test06();


	system("pause");
	return 0;
}

Use the initialization list

#include <iostream>
using namespace std;

class Person6
{
public:
	//初始化列表
	Person6(int a, int b) :m_A(a), m_B(b)
	{

	}

	int m_A;
	int m_B;
};

void test06()
{
	Person6 p(10, 20);
	cout << p.m_A << endl;
	cout << p.m_B << endl;
}

int main(void)
{
	test06();


	system("pause");
	return 0;
}

7. Class objects as class members

A member in a C++ class can be an object of another class, we call this member an object member

E.g:

class A {}
class B
{
    A a;
}

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

So when creating an object of B, who is the order of the construction and destruction of A and B?

#include <iostream>
#include <string>
using namespace std;

//手机类
class Phone
{
public:
	string p_name;

	Phone(string name) :p_name(name)
	{
		cout << "Phone 的构造函数" << endl;
	}

	~Phone()
	{
		cout << "Phone 的析构函数" << endl;
	}
};

//人类
class Person7
{
public:
	//m_Phone(p_Name)相当于 Phone m_Phone(p_Name)隐式转换法 
	Person7(string name, string p_Name) :m_Name(name), m_Phone(p_Name)
	{
		cout << "Person 的构造函数" << endl;
	}

	~Person7()
	{
		cout << "Person 的析构函数" << endl;
	}
	string m_Name;
	Phone m_Phone;

};

void test07()
{
	Person7 p("张三", "华为");
	cout <<p.m_Name<<"拿着" <<p.m_Phone.p_name<< endl;
}


int main(void)
{
	//当类中成员是其他类对象时,我们称该成员为 对象成员
	//构造的顺序是 :先调用对象成员的构造,再调用本类构造
	//析构顺序与构造相反
	test07();

	system("pause");
	return 0;
}

8. Static members

A static member is to add the keyword static before member variables and member functions, called static members

Static members are divided into:

  • Static member variable

    • All objects share the same data

    • Allocate memory during compilation

    • In-class declaration, out-of-class initialization

  • Static member function

    • All objects share the same function

    • Static member functions can only access static member variables

(1) Static member variables

#include <iostream>
using namespace std;

class Person8
{
public:
	static int m_A;   //静态成员变量

private:
	static int m_B;

};

//类内声明,类外初始化
int Person8::m_A = 100;
int Person8::m_B = 20;

void test8()
{
	Person8 p1;
	cout << p1.m_A << endl;

	Person8 p2;
	p2.m_A = 200;

	cout << p1.m_A << endl;   //共享一份数据

	//通过类名访问
	cout << Person8::m_A << endl;

	//私有权限访问不到
	//cout << Person8::m_B << endl;
}


int main(void)
{
	test8();

	system("pause");
	return 0;
}

(2) Static member function

#include <iostream>
using namespace std;

class Person9
{
public:
	//静态成员函数
	static void func()
	{
		m_A = 100;
		//m_B = 10;    //错误,不可以访问非静态成员变量
		//因为静态成员函数不属于任何一个对象,而非静态成员变量必须有一个对象去指向,产生矛盾
		cout << "void func调用" << endl;
	}
	
	//静态成员变量
	static int m_A;
	int m_B;

private:
	static void func2()
	{
		cout << "void func2调用" << endl;
	}
};

//静态成员变量初始化
int Person9::m_A = 10;

void test9()
{
	//静态成员变量两种访问方式

	//1、通过对象
	Person9 p;
	p.func();

	//2、通过类名
	Person9::func();

	//Person9::func2();   //私有权限访问不到
}


int main(void)
{
	test9();

	system("pause");
	return 0;
}

Static member functions cannot access non-static member variables. Because static member functions do not belong to any object, non-static member variables must have an object to point to, which creates contradictions.

Guess you like

Origin blog.csdn.net/Zhouzi_heng/article/details/113664706