【c++】复制构造函数|类型转换构造函数|析构函数

本文详细介绍了C++中的复制构造函数、类型转换构造函数和析构函数。复制构造函数用于同类对象初始化,类型转换构造函数实现类型自动转换,析构函数则在对象消亡时自动调用,用于清理工作。文章通过实例展示了这些函数的使用和调用情况。

复制构造函数

只有一个参数,即完成对同类对象的引用
形如X::X( X& )或X::X(const X &),二者选一,后者能一常量对象作为参数
如果没有定义复制构造函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能。

class Complex{
	private:
		double real, imag;
};
Complex c1;//调用缺省无参构造函数
Complex c2(c1);//调用缺省的复制构造函数,将c2初始化成hec1一样

如果定义的自己的复制构造函数,那么默认的复制构造函数不存在。

class Complex{
	public:
		double real, imag;
		Complex(){}
		Complex(const Complex & c){
			real = c.real;
			imag = c.imag;
			cout<<"copy constructor called";
		}
};
Complex c1;
Complex c2(c1);

不允许有形如X::X(X)的构造函数

class CSample{
	CSample(CSample c){
	}//错,不允许这样的构造函数
}

复制构造函数其作用的三种情况

  1. 当用一个对象去初始化同类的另一个对象时。
Complex c2(c1);
Complex c2 = c1;//初始化语句,非赋值语句,和上面那一句完全等价
  1. 如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用。
class A
{
	public:
	A(){};
	A(A & a){
		cout<<"Copy constructor called"<<endl;
	}
};
void Func(A a1){}
//在这个函数中参数是A的对象,因为函数要生成一个形参。
//这个形参就是通过类A的复制构造函数生成的,而复制构造函数需要接受一个参数。
//这个参数就是下面的a2
int main(){
	A a2;
	Func(a2);
	return 0;
}
//一般情况下,实参会与传递到函数中的形参相等,但是在这种情况下就不一定相等了
//因为这里的a1是由我们自己编写的复制构造函数来生成的
//而复制构造函数并没有进行复制的工作,生成的a1只是打印出一句话;
//程序输出结果为:"Copy constructor called"
//如果我们自己不写复制构造函数,用类默认的复制构造函数a1就会等于a2,因为默认的一定会进行复制工作
  1. 如果函数的返回值是类A的对象,则函数返回时,A的复制构造函数将被调用
class A
{
	public:
	int v;
	A(int n){v = n;};
	A(A & a){
		v =  a.v;
		cout<<"Copy constructor called"<<endl;
	}
};
A Func(){
	A b(4);
	return b;
}
int main(){
	cout << Func().v << endl;
	return 0;
}
//输出结果:
//Copy constructor called
//4

类型转换构造函数

  1. 实现类型的自动转换
  2. 只有一个参数
  3. 不是复制构造函数
  4. 编译系统会自动调用->转换构造函数->建立一个临时对象/临时变量
class Complex{
	public:
		double real, imag;
		//1.类型转换构造函数
		Complex(int i){
			cout<<"IntConstructor called"<<endl;
			real  = i; imag = 0;
		}
		//2.
		Complex(double r, double i){
			real = r;
			imag = i;
		}
}
int main(){
	Complex c1(7, 8);//2
	
	//1:初始化的过程中调用类型转换构造函数的时候不会生成一个临时对象
	Complex c2 = 12;
	
	//赋值语句,会自动调用类型转换函数,会生成一个临时对象
	//9被自动转换成一个临时Complex对象,9被当成实参输入进Complex函数
	//然后再把生成的临时对象赋值给c1
	c1 = 9;
	cout<<c1.real<<","<<c1.imag<<endl;
	return 0;
}
//输出:
//IntConstructor called
//IntConstructor called
//9,0

析构函数(Destructor)

构造函数名字与类名相同,可以有参数不能有返回值,可以有多个构造函数,用来初始化对象。
析构函数名字与类名相同,在前面会加上“~”,没有参数和返回值,一个类最多只能有一个析构函数。
对象消亡时,自动被调用。在对象消亡前做善后工作,释放分配的空间。
定义类的时候没写析构函数,则编译器生成缺省析构函数,默认析构函数什么都不做,不涉及释放用户申请的内存释放等清理工作。
用户如果自己定义了析构函数,则编译器不生成缺省析构函数。

class String{
	private:
		char *p;
	public:
		String(){
			p = new char[10];
		}
		~String();
};
String::~String(){
	delete[] p;
}

对象数组生命期结束时,对象数组的每个元素的析构函数都会被调用

class Ctest{
	public:
		~Ctest(){ cout<<"destructor called"<<endl;}
};
int main(){
	Ctest array[2];
	cout<<"End Main"<<endl;
	return 0;
}
//程序结束,自动释放调用析构函数
//输出:
//End Main
//destructor called
//destructor called

delete运算导致析构函数调用

Ctest * pTest;
pTest = new Ctest;//构造函数调用
delete pTest;//析构函数调用

pTest = new Ctest[3];//构造函数调用3次
delete[] pTest;//析构函数调用3次

例子:

class Demo {
		int id;
	public:
		Demo( int i )
		{
			id = i;
			cout << “id=<< id << “ Constructed” << endl;
		}
		~Demo()
		{
			cout << “id=<< id << “ Destructed” << endl;
		}
};
Demo d1(1);
void Func(){
	static Demo d2(2);//d2是静态变量,所以func运行完还会存在,消亡会在整个程序结束后
	Demo d3(3);//d3的作用域只在这个函数里
	cout << “Func” << endl;
}
int main (){
	Demo d4(4);
	d4 = 6;//生成一个临时的对象,然后赋值完成后,把临时对象析构
	cout << “main” << endl;
	{ Demo d5(5);}//有一个自己的作用域,离变量最近的花括号就是作用域,作用域又可以表示这个变量的作用周期,所以结束后析构d5
	Func();
	cout << “main ends” << endl;
	return 0;
}
//还未消亡的对象:d1 d2 d4
//先让d4析构掉,再把d2这个静态对象析构掉,最后析构全局对象d1
//先被构造的对象会最后被析构掉,这也是c++语法的一个思想

输出:
id=1 Constructed
id=4 Constructed
id=6 Constructed
id=6 Destructed
main
id=5 Constructed
id=5 Destructed
id=2 Constructed
id=3 Constructed
Func
id=3 Destructed
main ends
id=6 Destructed
id=2 Destructed
id=1 Destructed

此外,析构函数和构造函数在不同编译器中的表现,个别调用情况不一致

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值