Skip to content

Wiki

Object initialization and cleanup

About 9 min read

Existing English translations were reused. 3 current source blocks remain in Simplified Chinese, without triggering paid translation.

1.Object initialization and cleanup

  • In everyday life, most of the electronic products we buy come with factory settings, and one day when we no longer use them, we will also delete some of our own information and data to ensure security.
  • C++ The object-oriented concept in this comes from everyday life; every object also has initial settings as well as settings for cleaning up data before the object is destroyed.

1.1.Constructors and destructors

Objects'initialization and cleanupare also two very important security issues

​ An object or variable has no initial state, and the consequences of using it are unknown.

​ Similarly, if an object or variable is not cleaned up promptly after use, it can also cause certain security issues.

c++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。

The initialization and cleanup of objects are things the compiler forces us to do, so ifwe do not provide a constructor and destructor, the compiler will provide them.

The constructors and destructors provided by the compiler are empty implementations.

  • Constructor: Its main purpose is to assign values to the object's member attributes when the object is created. The constructor is automatically called by the compiler and does not need to be called manually.
  • Destructor: Its main purpose is that before the objectis destroyedthe system automatically calls it to perform some cleanup work.

Constructor syntax:类名(){}

  1. Constructors do not have a return value and do not use void.
  2. The function name is the same as the class name.
  3. Constructors can have parameters, so overloading can occur.
  4. When the program calls an object, it automatically calls the constructor; there is no need to call it manually, and it is called only once.

Destructor syntax: ~类名(){}

  1. A destructor has no return value and void is not written either.
  2. The function name is the same as the class name, with the symbol ~ prefixed to the name.
  3. A destructor cannot have parameters, so overloading cannot occur.
  4. The program automatically calls the destructor before the object is destroyed; there is no need to call it manually, and it is called only once.
class Person
{
public:
	//构造函数
	Person()
	{
		cout << "Person的构造函数调用" << endl;
	}
	//析构函数
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}

};

void test01()
{
	Person p;
}

int main() {
	
	test01();


	return 0;
}

Operation result:

Person的构造函数调用
Person的析构函数调用

1.2.Classification and invocation of constructors

Two classification methods:

​ Divided by parameters: parameterized constructor and parameterless constructor

​ Divided by type into: ordinary constructors and copy constructors

Three invocation methods:

​ Bracket method

​ Display method

​ Implicit conversion method

Example:

//1、构造函数分类
// 按照参数分类分为 有参和无参构造   无参又称为默认构造函数
// 按照类型分类分为 普通构造和拷贝构造

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;
};

//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);
}

int main() {

	test01();
	//test02();


	return 0;
}

Operation result:

无参构造函数!
析构函数!

1.3.Timing of Copy Constructor Invocation

C++There are usually three situations for the call timing of the copy constructor.

  • Use an already created object to initialize a new object
  • Pass values to function parameters by value
  • Return a local object by value

Example:

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

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

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

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

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

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

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

int main() {

	//test01();
	//test02();
	test03();


	return 0;
}

Running result (the address may differ on each run; denoted below by <地址>):

无参构造函数!
<地址>
<地址>
析构函数!

1.4.Constructor calling rules

默认情况下,c++编译器至少给一个类添加3个函数

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

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

3. Default copy constructor, which performs value copying of attributes.

The constructor invocation rules are as follows:

  • 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造

  • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数

Example:

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;
};

void test01()
{
	Person p1(18);
	//如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
	Person p2(p1);

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

void test02()
{
	//如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
	Person p1; //此时如果用户自己没有提供默认构造,会出错
	Person p2(10); //用户提供的有参
	Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供

	//如果用户提供拷贝构造,编译器不会提供其他构造函数
	Person p4; //此时如果用户自己没有提供默认构造,会出错
	Person p5(10); //此时如果用户自己没有提供有参,会出错
	Person p6(p5); //用户自己提供拷贝构造
}

int main() {

	test01();


	return 0;
}

Operation result:

有参构造函数!
拷贝构造函数!
p2的年龄为: 18
析构函数!
析构函数!

1.5.Deep Copy and Shallow Copy

Shallow and deep copy is a classic interview question and also a common pitfall.

Shallow copy: a simple assignment copy operation

Deep copy: reallocate space in the heap area and perform the copy operation.

Example:

class Person {
public:
	//无参(默认)构造函数
	Person() {
		cout << "无参构造函数!" << endl;
	}
	//有参构造函数
	Person(int age ,int height) {
		
		cout << "有参构造函数!" << endl;

		m_age = age;
		m_height = new int(height);
		
	}
	//拷贝构造函数  
	Person(const Person& p) {
		cout << "拷贝构造函数!" << endl;
		//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
		m_age = p.m_age;
		m_height = new int(*p.m_height);
		
	}

	//析构函数
	~Person() {
		cout << "析构函数!" << endl;
		if (m_height != NULL)
		{
			delete m_height;
		}
	}
public:
	int m_age;
	int* m_height;
};

void test01()
{
	Person p1(18, 180);

	Person p2(p1);

	cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;

	cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}

int main() {

	test01();


	return 0;
}

Operation result:

有参构造函数!
拷贝构造函数!
p1的年龄: 18 身高: 180
p2的年龄: 18 身高: 180
析构函数!
析构函数!

Summary: If an attribute is allocated on the heap, you must provide a copy constructor yourself to prevent problems caused by shallow copying.

1.6.Initialize List

Function:

C++ Provides initializer list syntax for initializing properties

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

Example:

class Person {
public:

	////传统方式初始化
	//Person(int a, int b, int c) {
	//	m_A = a;
	//	m_B = b;
	//	m_C = c;
	//}

	//初始化列表方式初始化
	Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}
	void PrintPerson() {
		cout << "mA:" << m_A << endl;
		cout << "mB:" << m_B << endl;
		cout << "mC:" << m_C << endl;
	}
private:
	int m_A;
	int m_B;
	int m_C;
};

int main() {

	Person p(1, 2, 3);
	p.PrintPerson();


	return 0;
}

Operation result:

mA:1
mB:2
mC:3

1.7.Class Objects as Class Members

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

For example:

class A {}
class B
{
    A a;
}

Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.

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

Then, when creating an object of B, what is the order of construction and destruction between A and B—which comes first and which comes later?

Example:

class Phone
{
public:
	Phone(string name)
	{
		m_PhoneName = name;
		cout << "Phone构造" << endl;
	}

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

	string m_PhoneName;

};

class Person
{
public:

	//初始化列表可以告诉编译器调用哪一个构造函数
	Person(string name, string pName) :m_Name(name), m_Phone(pName)
	{
		cout << "Person构造" << endl;
	}

	~Person()
	{
		cout << "Person析构" << endl;
	}

	void playGame()
	{
		cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手机! " << endl;
	}

	string m_Name;
	Phone m_Phone;

};
void test01()
{
	//当类中成员是其他类对象时,我们称该成员为 对象成员
	//构造的顺序是 :先调用对象成员的构造,再调用本类构造
	//析构顺序与构造相反
	Person p("张三" , "苹果X");
	p.playGame();

}

int main() {

	test01();


	return 0;
}

Operation result:

Phone构造
Person构造
张三 使用苹果X 牌手机!
Person析构
Phone析构

1.8.Static member

A static member is a member variable or member function with the keyword static added before it, and is called a static member.

Static members are divided into:

  • Static member variables
    • All objects share the same data
    • Memory is allocated during the compilation phase
    • Declared within the class, initialized outside the class
  • Static member functions
    • All objects share the same function
    • Static member functions can only access static member variables

**Example 1:** Static member variables

class Person
{
	
public:

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

	//静态成员变量特点:
	//1 在编译阶段分配内存
	//2 类内声明,类外初始化
	//3 所有对象共享同一份数据

private:
	static int m_B; //静态成员变量也是有访问权限的
};
int Person::m_A = 10;
int Person::m_B = 10;

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

	//1、通过对象
	Person p1;
	p1.m_A = 100;
	cout << "p1.m_A = " << p1.m_A << endl;

	Person p2;
	p2.m_A = 200;
	cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据
	cout << "p2.m_A = " << p2.m_A << endl;

	//2、通过类名
	cout << "m_A = " << Person::m_A << endl;

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

int main() {

	test01();


	return 0;
}

Operation result:

p1.m_A = 100
p1.m_A = 200
p2.m_A = 200
m_A = 200

**Example 2:** Static member function

class Person
{

public:

	//静态成员函数特点:
	//1 程序共享一个函数
	//2 静态成员函数只能访问静态成员变量
	
	static void func()
	{
		cout << "func调用" << endl;
		m_A = 100;
		//m_B = 100; //错误,不可以访问非静态成员变量
	}

	static int m_A; //静态成员变量
	int m_B; // 
private:

	//静态成员函数也是有访问权限的
	static void func2()
	{
		cout << "func2调用" << endl;
	}
};
int Person::m_A = 10;

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

	//1、通过对象
	Person p1;
	p1.func();

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

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

int main() {

	test01();


	return 0;
}

Operation result:

func调用
func调用