C++ 构造函数和析构函数

807 阅读6分钟

C++是面向对象的编程语言呢,具有类class,类的初始化就需要调用类的构造函数,默认调用类的无参构造函数,如果重写了类的构造函数,会覆盖类的默认构造函数。

构造函数

构造函数是一种特殊的成员函数,除具有一般成员函数的特性外,还具有如下特殊性质:

1)、构造函数的函数名必须与其类名相同;

2)、不包含任何返回类型,也不能写成void类型;

3)、一个类可以有多个构造函数(即构造函数重载),也可以没有构造函数(在这种情况下,系统会自动为类创建一个默认构造函数,该构造函数无参数4) 、构造函数可以有参数,也可以没有参数;);

4)、在创建对象时,系统会自动调用构造函数(例:执行 Person abc; 语句时就自动调用了构造函数),且只调用一次;

5)、用new运算符动态创建对象时也会自动调用构造函数;

6)、通常,设为public类型。

class Student
{
public:
	void mprintf(){
		cout << "name:" << name << "--------" << "age:" << age << endl;
	}
	//覆盖该类的默认构造函数
	Student(char*name, int age){
		this->name = name;
		this->age = age;
	}

private:
	char* name;
	int age;

};

void main(){

	Student s("xiaoMing",15);
	s.mprintf();

	getchar();
}

C++.png

析构函数

析构函数存在的必要性:

a)、如果构造函数中使用了new 命令申请了堆空间,在对象撤消时,就要用delete命令归还对象所占空间。否则,造成内存泄漏。

b)、退出程序时,进行文件内容检查,提示存盘操作 。

析构函数是一种特殊的成员函数,除具有一般成员函数的特性外,还具有如下特性:

1)、析构函数的函数名必须与其类名相同,不能指定返回类型,也不能使用void。为了能与构造函数相区别,要在函数名前加~(波浪符);

2)、析构函数没有参数,一个类中只能拥有一个析构函数,所以析构函数不能重载。定义格式为: ~类名(){ 函数体 } ;

3)、如果程序员没有定义类的析构函数,系统会自动为类创建一个默认析构函数,形式为: ~类名(){ };

4)、通常为public类型,系统在撤消对象时,自动调用析构函数。也允许显式调用;

5)、用delete运算符删除对象时也会自动调用析构函数 。

class Student
{
public:
	void mprintf(){
		cout << "name:" << name << "--------" << "age:" << age << endl;
	}
	//覆盖该类的默认构造函数
	Student(char*name, int age){
		this->name = name;
		this->age = age;
		cout << "构造函数" << endl;
	}
	//析构函数
	~Student(){
		cout << "析构函数" << endl;
	}

private:
	char* name;
	int age;

};

void func(){
	Student s("xiaoMing", 15);
	s.mprintf();
}

void main(){

	func();

	getchar();
}

C++.png

当对象要被系统释放时,析构函数被调用,在析构函数中可以做一些善后处理处理的工作,如释放内存。

拷贝构造函数

概念:用一个已有的对象来初始化一个被创建的同类对象,是一种特殊的成员函数。

声明的一般格式: 类名(类名 & 对象名);

利用拷贝构造函数可以实现同类对象的数据传递, 即对象的“克隆”。(注意:与对象赋值的不同) (相对于拷贝构造函数,前面介绍的构造函数被称为普通构造函数)

拷贝构造函数的性质 :

1)、函数名必须与类名相同,并且不指定返回类型;

2)、只有一个参数,是同类的对象的引用;

3)、每一个类中都必须有一个拷贝构造函数。如果类中没有声明拷贝构造函数,编译器就会自动生成一个具有上述形式的公有的拷贝构造函数。

拷贝构造函数被调用的场景:

1)、声明时赋值 Student s1 = s;

2)、作为参数传入,实参给形参赋值 func(s);

3)、作为函数返回值返回,给变量初始化赋值 Student s1 = func(s);

浅拷贝和深拷贝

如果在类的定义里没有提供拷贝构造函数,C++会提供一个默认的拷贝构造函数; 默认拷贝构造函数的拷贝方式是各成员逐一复制,这称为是浅拷贝; 在某些情况下,浅拷贝会产生问题,例如:当一个类在它的构造函数动态分配了内存资源,浅拷贝只会复制该资源的地址,而不会为新建的对象重新分配一个资源,因此,会出现多个对象指向同一个堆空间的地址的现象。

浅拷贝也就是值拷贝:

class Student
{
public:
	void mprintf(){
		cout << "name:" << name << "--------" << "age:" << age << endl;
	}
	//覆盖该类的默认构造函数
	Student(char*name, int age){
		this->name = name;
		this->age = age;
		cout << "构造函数" << endl;
	}
	//默认拷贝构造函数,就是值拷贝
	Student(const Student &obj){
		this->name = obj.name;
		this->age = obj.age;
		cout << "拷贝构造函数" << endl;
	}
	//析构函数
	~Student(){
		cout << "析构函数" << endl;
	}

private:
	char* name;
	int age;

};

void func(Student s){
	s.mprintf();
}

void main(){
	Student s("xiaoMing", 15);
	func(s);

	getchar();
}

C++.png

浅拷贝(值拷贝)问题

class Student
{
public:
	void mprintf(){
		cout << "name:" << name << "--------" << "age:" << age << endl;
	}
	//覆盖该类的默认构造函数
	Student(char*name, int age){
		this->name = (char*)malloc(100);
		strcpy(this->name, name);
		this->age = age;
		cout << "构造函数" << endl;
	}
	////默认拷贝构造函数,就是值拷贝
	//Student(const Student &obj){
	//	this->name = obj.name;
	//	this->age = obj.age;
	//	cout << "拷贝构造函数" << endl;
	//}
	//析构函数
	~Student(){
		cout << "析构函数" << endl;
		free(this->name);
	}

private:
	char* name;
	int age;

};

void func(){
	Student s("xiaoMing", 15);
	Student s1 = s;
	s1.mprintf();
}

void main(){
	
	func();
	
	getchar();
}

C++.png

以上就是因为浅拷贝导致中止。错误的原因:两个对象的指针指向同一存储地址,析构时要求释放两次。

解决问题的方法:深拷贝

在拷贝构造函数中,显式地为每个拷贝对象申请资料,这种拷贝构造方式称为深拷贝。

class Student
{
public:
	void mprintf(){
		cout << "name:" << name << "--------" << "age:" << age << endl;
	}
	//覆盖该类的默认构造函数
	Student(char*name, int age){
		this->name = (char*)malloc(100);
		strcpy(this->name, name);
		this->age = age;
		cout << "构造函数" << endl;
	}
   //深拷贝
	Student(const Student &obj){
		//复制name属性
		int len = strlen(obj.name);
		this->name = (char*)malloc(len + 1);
		strcpy(this->name, obj.name);
		this->age = obj.age;
		cout << "拷贝构造函数" << endl;
	}
	//析构函数
	~Student(){
		cout << "析构函数" << endl;
		free(this->name);
	}

private:
	char* name;
	int age;

};

void func(){
	Student s("xiaoMing", 15);
	Student s1 = s;
	s1.mprintf();
}

void main(){
	
	func();
	
	getchar();
}

C++.png