C++之构造函数与析构函数

构造函数

所谓构造函数就是跟类名相同的函数,可以带有参数也可以不带参数。可以通过参数的不用,来区分调用哪个构造函数

类的构造方法的概念和作用

  • 构造方法负责对象初始化工作,为对象属性赋合适的初始值
  • 创建对象时,其类的构造方法确保在用户操作对象之前,系统保证初始化的进行

构造方法的语法规则

  • 构造函数名和类名一致
  • 没有返回类型
  • 方式实现主要为字段赋值

demo

#include <iostream>

using namespace std;

class Person{
	char *name;
	int age;
	char *work;

public:
	Person(){
		cout<<"Person()"<<endl;
	}

	Person(char* name, int age, char* work = "none"){
		cout<<"Person(char*, int)"<<endl;
		this->name = name;
		this->age = age;
		this->work = work;
	}

	void printfInfo(){
		cout<<"work = "<<work<<" name = "<<name<<" age = "<<age<<endl;
	}
};

int main(int argv, char** argc)
{
	Person per("zhangsan",18);
	Person per1;	//这是调用没有参数的构造体
	Person per2();	//这是定义一个函数,Person型的函数,相当于int fun();
	per.printfInfo();
}

程序结果

Person(char*, int)
Person()
work = none name = zhangsan age = 18

在C++中,可以通过指针实例化类,程序结束后,指针会被自动释放。如果你想要手动释放的话,可以使用delete命令。

demo

#include <iostream>

using namespace std;

class Person{
	char *name;
	int age;
	char *work;

public:
	Person(){
		cout<<"Person()"<<endl;
	}

	Person(char* name, int age, char* work = NULL){
		cout<<"Person(char*, int)"<<endl;
		this->name = name;
		this->age = age;
	}

	void printfInfo(){
		cout<<"work = "<<work<<" name = "<<name<<" age = "<<age<<endl;
	}
};

int main(int argv, char** argc)
{
	Person per("zhangsan",18);
	Person per1;	//这是定义没有参数的构造体
	Person per2();	//这是定义一个函数,Person型的函数

	/*通过指针实例化类*/

	/*以下两种实例方法是等同的,都是调用没有参数的构造体*/
	Person *per3 = new Person;
	Person *per4 = new Person();

	Person *per5 = new Person[2];//也可以定义成数组

	Person *per6 = new Person("lisi", 16, "student");//也可以添加参数

	/*程序结束后,指针会被自动释放,如果像手动释放的话用以下方法*/

	delete per3;
	delete []per5;//释放数组的方法

	per6->printfInfo();
	
	per.printfInfo();
}

程序结果:

Person(char*, int)
Person()
Person()
Person()
Person()
Person()
Person(char*, int)

在main函数中用new函数开辟的空间,会自动释放内存。但是在构造函数中利用new函数开辟了一个堆空间和子程序中的开辟空间需要我们自己释放。如果不释放则会占用我们的内存。

demo

#include <iostream>
#include <string.h>
#include <unistd.h>


using namespace std;

class Person{
	char *name;
	int age;
	char *work;

public:
	Person(){
		cout<<"Person()"<<endl;
	}

	Person(char* name){
		//cout<<"Person(char*, int)"<<endl;
		
		/*在构造函数中利用new函数开辟了一个堆空间需要我们自己释放*/
		this->name = new char[strlen(name)+1];
		strcpy(this->name,name);//将参数名字放入开辟的空间之内

		this->work = NULL;//没有work这个参数,要赋值一个null给他,否则析构函数中会出现段错误
	}

	Person(char* name, int age, char* work = "none"){
	//	cout<<"Person(char*, int)"<<endl;
		
		this->name = new char[strlen(name)+1];
		strcpy(this->name,name);
		
		this->work = new char[strlen(work)+1];
		strcpy(this->work,work);
		
		this->age = age;
	}
 
	void printfInfo(){
		cout<<" name = "<<name<<" age = "<<age<<endl;
	}
};

int testFun()
{
	Person per("zhangsan",18);

	Person *per6 = new Person("lisi", 16, "student");//子程序中的空间需要我们自己释放
	delete per6;
}

int main()
{
	int i;
	for(i = 0; i < 100000; i++)
		testFun();//在执行该函数的时候一直在消耗空间内存,并没有去释放构造函数中new出来的空间
	sleep(10);
	return 0;
}

可以通过free命令查看当前空闲内存空间的大小。

free -m #-m是指输出的信息以兆为单位;

从以下可以看出,在运行过程中会占用内存空间。

book@100ask:~$ free -m//运行前
              total        used        free      shared  buff/cache   available
Mem:           1959        1129         350          15         479         642
Swap:          8581         171        8410
book@100ask:~$ free -m//运行时
              total        used        free      shared  buff/cache   available
Mem:           1959        1144         335          15         479         627
Swap:          8581         171        8410
book@100ask:~$ free -m//运行后
              total        used        free      shared  buff/cache   available
Mem:           1959        1129         350          15         479         642
Swap:          8581         171        8410
book@100ask:~$ 

那怎么去释放构造函数中的内存空间呢?有两种方法:

第一种方法就是在引入一个人free函数去释放空间,在free函数中去delete掉开辟的空间,然后再在子程序中调用free函数就可以了。但是这种方法非常麻烦,看起来也不高级。

第二种方法就是引入析构函数,那么什么是析构函数呢?

析构函数

析构函数:是一个特殊的成员函数,它的作用与构造函数相反,它的名字是类名的前面加一个“~”符号。

析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用。程序设计者事先设计好析构函数,以完成所需的功能,只要对象的生命期结束,程序就自动执行析构函数来完成这些工作。

注意:析构函数不返回任何值,没有函数类型,也没有函数参数。因此它不能被重载。一个类可以有多个构造函数,但只能有一个析构函数。

demo

#include <iostream>
#include <string.h>
#include <unistd.h>

using namespace std;

class Person{
 	char *name;
 	int age;
	char *work;

public:
	Person(){
		cout<<"Person()"<<endl;
	}

	Person(char* name){
		cout<<"Person(char*)"<<endl;
		
		/*开辟一个空间储存名字,但是子程序中的空间需要我们自己释放*/
		this->name = new char[strlen(name)+1];
		strcpy(this->name,name);//将参数名字放入开辟的空间之内

		this->work = NULL;//没有work这个参数,要赋值一个null给他,否则析构函数中会出现段错误
	}

	Person(char* name, int age, char* work = "none"){
		cout<<"Person(char*, int)"<<endl;
		
		this->name = new char[strlen(name)+1];
		strcpy(this->name,name);
		
		this->work = new char[strlen(work)+1];
		strcpy(this->work,work);
		
		this->age = age;
	}

	~Person(){//析构函数,有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
		if(this->name){
			cout<<"name ="<<name<<endl;
			delete this->name;
		}
		if(this->work){
			cout<<"work ="<<work<<endl;
			delete this->work;
		}
	}

	void printfInfo(){
		cout<<" name = "<<name<<" age = "<<age<<endl;
	}
};

int testFun()
{
	Person per("zhangsan",18);
	Person *per6 = new Person("lisi", 16, "student");//也可以添加参数
	delete per6;//手动释放空间
}

int main()
{
	testFun();
	sleep(10);
	return 0;
}

程序结果:从结果可以看出,当testFun()函数执行完之后,系统会自动调用析构函数,释放内存空间。而在testFun()利用new实例化的对象则需要手动调用delete释放空间,否则testFun()函数执行完成之后也不会调用析构函数释放该空间,只能等到程序结束之后系统来释放。

Person(char*, int)
Person(char*, int)
~Person()
name =lisi
work =student
~Person()
name =zhangsan
work =none

内存空闲空间可以看出,调用析构函数之后,就没有占用内存空间了。

book@100ask:~$ free -m  #运行前
              total        used        free      shared  buff/cache   available
Mem:           1959        1126         370          15         461         645
Swap:          8581         171        8410
book@100ask:~$ free -m  #运行中
              total        used        free      shared  buff/cache   available
Mem:           1959        1126         370          15         462         645
Swap:          8581         171        8410
book@100ask:~$ free -m  #运行后
              total        used        free      shared  buff/cache   available
Mem:           1959        1126         371          15         462         646
Swap:          8581         171        8410
book@100ask:~$ 

具体地说如果出现以下几种情况,程序就会执行析构函数

①如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
②static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。
③如果定义了一个全局对象,则在程序的流程离开其作用域时(如main函数结束或调用exit函数) 时,调用该全局对象的析构函数。
④如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。

拷贝函数

Person per("zhangsan", 18, "teacher");

/*打印的与per内容一致,会调用默认的拷贝函数*/
Person per1(per);//值拷贝,有一个隐患,会对同一个空间释放两次,不建议默认调用拷贝构造函数
//可以利用引用拷贝,解决该隐患

可以使用拷贝函数解决一个空间被释放两次的隐患。

拷贝函数原型

Person(Person &per){//拷贝函数,这样就不.会对同一个地址析构两次
		cout<<"Person(Person &)"<<endl;
		
		this->name = new char[strlen(per.name)+1];
		strcpy(this->name,per.name);
		
		this->work = new char[strlen(per.work)+1];
		strcpy(this->work,per.work);
		
		this->age = per.age;
	}

构造函数被调用的顺序

demo

#include <iostream>
#include <string.h>
#include <unistd.h>

using namespace std;

class Person{
	char *name;
	int age;
	char *work;

public:
	Person(){
		name = NULL;
		work = NULL;
	}

	Person(char* name){
		/*开辟一个空间储存名字,但是子程序中的空间需要我们自己释放*/
		this->name = new char[strlen(name)+1];
		strcpy(this->name,name);//将参数名字放入开辟的空间之内

		this->work = NULL;//没有work这个参数,要赋值一个null给他,否则析构函数中会出现段错误
	}

	Person(char* name, int age, char* work = "none"){
		cout<<"Person(char*, int)"<<endl;
		
		this->name = new char[strlen(name)+1];
		strcpy(this->name,name);
		
		this->work = new char[strlen(work)+1];
		strcpy(this->work,work);
		
		this->age = age;

		printfInfo();
	}

	Person(Person &per){//拷贝函数
		cout<<"Person(Person &)"<<endl;
		
		this->name = new char[strlen(per.name)+1];
		strcpy(this->name,per.name);
		
		this->work = new char[strlen(per.work)+1];
		strcpy(this->work,per.work);
		
		this->age = per.age;
	}

	~Person(){//析构函数,有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
		cout<<"~Person()"<<endl;
		if(this->name){
			cout<<"name ="<<name<<endl;
			delete this->name;
		}
		if(this->work){
			cout<<"work ="<<work<<endl;
			delete this->work;
		}
	}
	
	void printfInfo(){
		cout<<" name = "<<name<<" age = "<<age<<"work = "<<work<<endl;
	}
};

Person per_g("per_g",10); 

void per_func()
{
	Person per_func("per_func",11);
	static Person per_func_s("per_func_s",12);
}

int main(int argc, char** argv)
{
	Person per_main("per_main", 13);
	static Person per_main_s("per_main_s", 14);
	
	for(int i=0; i<2; i++){
		per_func();
		Person per_for("per_for",i);
	}
	return 0;
}

程序结果:
析构函数不会销毁static类型的实例化对象开辟的空间。所以static类型的实例化对象只会被调用一次。
构造顺序:按运行中定义对象的顺序调用构造函数 ,全局对象在main函数执行前被调用。

Person(char*, int)
 name = per_g age = 10work = none
Person(char*, int)
 name = per_main age = 13work = none
Person(char*, int)
 name = per_main_s age = 14work = none
Person(char*, int)
 name = per_func age = 11work = none
Person(char*, int)
 name = per_func_s age = 12work = none
~Person()
name =per_func
work =none
Person(char*, int)
 name = per_for age = 0work = none
~Person()
name =per_for
work =none
Person(char*, int)
 name = per_func age = 11work = none
~Person()
name =per_func
work =none
Person(char*, int)
 name = per_for age = 1work = none
~Person()
name =per_for
work =none
~Person()
name =per_main
work =none
~Person()
name =per_func_s
work =none
~Person()
name =per_main_s
work =none
~Person()
name =per_g
work =none

在一个类中调用另一个类

要调用对象成员的其他构造函数,可以这样写:

class Student{
private:
    person id;
public:        
    Student(int sid):id(sid){}
}

构造函数的“{}”前加上“:”,加上成员的初始化代码。
对象成员:定义时的构造顺序与上述初始化顺序无关。

demo

#include <iostream>
#include <string.h>
#include <unistd.h>

using namespace std;

class Person{
	char *name;
	int age;
	char *work;

public:
	Person(){
		name = NULL;
		work = NULL;
	}

	Person(char* name){
		/*开辟一个空间储存名字,但是子程序中的空间需要我们自己释放*/
		this->name = new char[strlen(name)+1];
		strcpy(this->name,name);//将参数名字放入开辟的空间之内

		this->work = NULL;//没有work这个参数,要赋值一个null给他,否则析构函数中会出现段错误
	}

	Person(char* name, int age, char* work = "none"){
		cout<<"Person(char*, int)"<<endl;
		
		this->name = new char[strlen(name)+1];
		strcpy(this->name,name);
		
		this->work = new char[strlen(work)+1];
		strcpy(this->work,work);
		
		this->age = age;

		printfInfo();
	}

	Person(Person &per){//拷贝函数
		cout<<"Person(Person &)"<<endl;
		
		this->name = new char[strlen(per.name)+1];
		strcpy(this->name,per.name);
		
		this->work = new char[strlen(per.work)+1];
		strcpy(this->work,per.work);
		
		this->age = per.age;
	}

	~Person(){//析构函数,有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
		cout<<"~Person()"<<endl;
		if(this->name){
			cout<<"name ="<<name<<endl;
			delete this->name;
		}
		if(this->work){
			cout<<"work ="<<work<<endl;
			delete this->work;
		}
	}
	

	void printfInfo(){
		cout<<" name = "<<name<<" age = "<<age<<"work = "<<work<<endl;
	}
};

class Student{
private:
	Person father;
	Person mother;
	int student_id;

public:

	Student(){//当你提供了一个有参数的构造函数时,默认的无参构造函数就会消失,必须设置一个无参数的构造函数,这样才不会报错
		
	}
	
	Student(int id, char* father, char* mother, int father_age = 40, int mother_age = 39) : father(father,father_age), mother(mother,mother_age)
	{
		cout<<"Studen(int id, char* father, char* mother, int father_age = 40, int mother_age = 39)"<<endl;
	}

	~Student(){//析构函数的执行顺序与构造函数执行顺序相反
		cout<<"~Student()"<<endl;
	}
};

int main(int argc, char** argv)
{
	Student s(100, "bill", "lily");//调用构造函数
}

程序结果:由程序结果可以得出,调用构造函数的顺序与实例化对象的顺序有关。析构函数的执行顺序与构造函数执行顺序相反。

Person(char*, int)
 name = bill age = 40work = none
Person(char*, int)
 name = lily age = 39work = none
Studen(int id, char* father, char* mother, int father_age = 40, int mother_age = 39)
~Student()
~Person()
name =lily
work =none
~Person()
name =bill
work =none
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值