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

1. 系统提供构造函数规则

  1. 系统默认会提供默认无参构造函数、拷贝构造(浅拷贝)和析构函数
  2. 如果用户定义拷贝构造函数,c++不会再提供任何默认构造函数
  3. 如果用户定义了普通有参构造函数(非拷贝),C++不在提供默认无参构造,但是会提供默认拷贝构造(浅拷贝)

1.1 代码示例

	Person()	//< 默认构造,自定义后,则编译器不再提供默认构造函数
	{
		std::cout << "enter into default constructor\n";
		this->m_nAge = 0;
	}

	Person(const Person&per)	//< 拷贝构造函数,自定义后,编译器则不再提供
	{
		std::cout << "enter into copy constructor\n";
		this->m_nAge = per.m_nAge;
	}

	~Person()
	{
		std::cout << "enter into destructor\n";
	}

private:
	int m_nAge;
};

2. 默认构造函数

  1. 定义时的歧义:
class Person
{
public:
	Person()	//< 默认构造,自定义后,则系统不再提供默认构造函数
	{
		this->m_nAge = 0;
	}

	Person(int nAge = 10)		//< 带默认参数会和默认构造产生歧义
	{
		this->m_nAge = nAge;
	}

private:
	int m_nAge;
};

int main()
{
	Person p;	//< 报错:使用哪个默认构造,会有歧义
	
	return 0;
}

3. 拷贝构造函数

3.1 深拷贝和浅拷贝

  • 如果类成员变量中存在指向堆空间的数据,那浅拷贝会导致重复释放内存的异常(解决上述问题就需要深拷贝)

3.2 调用拷贝构造的时机

	Person()	//< 默认构造,自定义后,则编译器不再提供默认构造函数
	{
		std::cout << "enter into default constructor\n";
		this->m_nAge = 0;
	}

	Person(const Person&per)	//< 拷贝构造函数,自定义后,编译器则不再提供
	{
		std::cout << "enter into copy constructor\n";
		this->m_nAge = per.m_nAge;
	}

	~Person()
	{
		std::cout << "enter into destructor\n";
	}

private:
	int m_nAge;
};

void CallPer(Person per)
{
	std::cout << "enter into CallPer function\n";
}

Person ReturnPer(Person &per)
{
	return per;
}

int main()
{
	//! 1.用对象来初始化新建对象,会调用拷贝构造函数
	//Person p1;
	//Person p2 = p1;

	//! 2.对象以值传递的方式传给函数参数,会调用拷贝构造函数
	//Person p3;
	//CallPer(p3);	//< 函数参数会有1个临时变量来接受传递的对象 Person per = p3,则再次调用拷贝构造

	//! 3.函数以值传递的方式返回对象
	Person p4,p5;
	p5 = ReturnPer(p4);	//< 返回p4的引用per,用p5 = per 会调用拷贝构造函数

	return 0;
}

4. 带参构造函数

4.1 带单个参数


//! explicit关键字:适用于单参的构造函数,只能显示的调用构造函数,不能隐式的调用
class Person
{
public:
	Person()			
	{
		this->m_nAge = 0;
	}

	Person(int nAge)	//< 带单个参数的有参构造,不加关键字explicit,则容易发生隐士转换;如果加了explicit关键字,隐士转换会报错
	{
		this->m_nAge = nAge;
	}

private:
	int m_nAge;
};

int main()
{
	Person p = 10;	//< 隐士转换:将int类型转换成Person类型;等同于Person p = Person(10);Person(10)即隐式的调用了有参构造Person(int nAge)

	return 0;
}

5. 不使用拷贝构造和拷贝赋值运算符

5.1 方法一

将他们都声明为私有,不需要实现

缺点:万一有成员函数或者friend函数不小心调用它们,报错会推迟到链接的时候才会报错。通常报错越早越好

class CPerson
{
public:
	CPerson(){}
	~CPerson(){}
	void Display(const CPerson& p)
	{
		*this = p;	//<	成员函数调用拷贝赋值运算符(链接的时候才报错)
	}

private:
	CPerson(const CPerson&);
	CPerson& operator=(const CPerson&);
};

void Test01()
{
	CPerson p1;
	CPerson p2;
	//p1.Display(p2); 
}

5.2 方法二

为当前类设置一个专门为了阻止拷贝动作而设计的基类

注:当有成员函数或friend函数尝试拷贝当前类对象,编译器会试着生成一个copy构造函数和一个copy assignment操作符,这些函数的“编译器生成版”会尝试调用其基类的对应星弟,那些调动会被编译器拒绝,因为其基类的拷贝函数是private

class CUncopyable
{
protected:				//<	允许derived对象构造和析构
	CUncopyable(){}
	~CUncopyable(){}
private:
	CUncopyable(const CUncopyable&);			//< 组织copying
	CUncopyable& operator=(const CUncopyable&);
};

class CAnimal: private CUncopyable
{
public:
	CAnimal()
		:m_a(0)
	{

	}
	void Display(const CAnimal& p)
	{
		//*this = p;	//<	成员函数调用拷贝赋值运算符(编译的时候就报错)
		cout << m_a << endl;
	}

private:
	int m_a;
};

参考effective c++ 第三版 条款06 若不想使用编译器自动生成的函数,就该明确拒绝

6. 析构

6.1 别让异常逃离析构

  • 析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序(std::abort())。
  • 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。

参考:effective c++ 第三版 条款08:别让异常逃离析构函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值