c ++ :对象的构造和析构

目录

一、构造函数和析构函数

二、 构造函数的分类及调用

三、拷贝构造函数的调用时机

四、构造函数调用规则

五、深拷贝和浅拷贝

六、初始化列表

七、explicit关键字


 

对象的初始化和清理也是两个非常重要的安全问题,一个对象或者变量没有初始时,对其使用后果是未知,同样的使用完一个变量,没有及时清理,也会造成一定的安全问题。c++为了给我们提供这种问题的解决方案,构造函数析构函数,这两个函数将会被编译器自动调用,完成对象初始化和对象清理工作。

 

一、构造函数和析构函数

构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

析构函数主要用于对象销毁前系统自动调用,执行一些清理工作。

构造函数语法:

  1. 构造函数函数名和类名相同,没有返回值,不能有void,但可以有参数。
  2. ClassName(){ }

析构函数语法:

  1. 析构函数函数名是在类名前面加”~”组成,没有返回值,不能有void,不能有参数,不能重载。
  2. ~ClassName(){ }

 

二、 构造函数的分类及调用

  1. 按参数类型:无参构造函数、有参构造函数
  2. 按类型分类:普通构造函数、拷贝构造函数(复制构造函数)

例子:创立一个Person类

1、构造函数的分类

class Person
{
public:

	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}

	Person(int a)
	{
		cout << "Person的有参构造函数调用" << endl;
	}

	//拷贝构造  //值传递的本质 就是调用 拷贝构造函数
	Person(const Person &p)
	{
		cout << "Person的拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
	}

	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}

	int m_Age;
};

2、构造函数的调用

void test02()
{
//1 括号法

	Person p1(10); //有参构造函数调用
	p1.m_Age = 18;
	Person p2(p1);  //利用括号法  调用拷贝构造函数
	cout << "p2的年龄为: " << p2.m_Age << endl;

//注意事项1 : 不要利用括号法 调用默认构造函数 Person p(); 原因将代码看成 函数的声明,不会认为是在创建对象
	Person p();
	void func();

//2 显示法
	Person p3 = Person(10); //有参构造调用
	//显示法 调用  拷贝构造函数
	Person p4 = Person(p3);


	Person(10); //单独写 Person(10); 称为 匿名对象 特点:当本行执行完毕,立即释放
	cout << "aaaaaaaaa" << endl;

//注意事项2 : 不要利用拷贝构造函数 初始化匿名对象
	Person(p4); // 当写成Person(p4);  编译器会认为写了 Person p4  如果已经有p4就是重定义 匿名对象放到右值没问题


//3 隐式转换法 可读性低
	Person p5 = 10; //编译器隐式将代码转为 Person p5 = Person(10); 
	//利用隐式转换法  调用拷贝构造函数
	Person p6 = p5; // 隐式转为 Person p6 = Person(p5);
}

 

   b为A的实例化对象,A a = A(b) 和 A(b)的区别?

   当A(b) 有变量来接的时候,那么编译器认为是一个匿名对象,当没有变量来接的时候,编译器认为A(b) 等价于 A b.

 

三、拷贝构造函数的调用时机

值传递的本质:就是调用 拷贝构造函数

 

3种情况可能用到:

  1. 对象以值传递的方式传给函数参数
  2. 函数局部对象以值传递的方式从函数返回(vs debug模式下调用一次拷贝构造,qt不调用任何构造)
  3. 用一个对象初始化另一个对象

用代码解释上面3中情况:

class Person
{
public:

	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}

	Person(int a)
	{
		cout << "Person的有参构造函数调用" << endl;
	}

	//拷贝构造  //值传递的本质 就是调用 拷贝构造函数
	Person(const Person &p)
	{
		cout << "Person的拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
	}

	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}

	int m_Age;
};

//1、用已经创建好的对象 初始化新的对象
void test01()
{
	Person p1;
	p1.m_Age = 10;

	Person p2(p1);//拷贝构造函数调用
	cout << "p2的年龄为: " << p2.m_Age << endl;
}

//2、值传递的方式 给函数参数传值
void doWork( Person p)
{

}

void test02()
{
	Person p;
	doWork(p);
}

//3、以值的方式返回局部对象
Person doWork2()
{
	Person p;
	return p;
}

void test03()
{
	Person p = doWork2();
}

 

四、构造函数调用规则

  • 默认情况下,c++编译器至少为我们写的类增加3个函数:
  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对类中非静态成员属性简单值拷贝
  • 如果用户定义拷贝构造函数,c++不会再提供任何默认构造函数
  • 如果用户定义了普通构造(非拷贝),c++不在提供默认无参构造,但是会提供默认拷贝构造

 

五、深拷贝和浅拷贝

当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,深拷贝。

如果是深拷贝, 堆区的数据可能会被释放多次。

拷贝构造函数例子:

class Person{
public:
	Person(char* name,int age){
		pName = (char*)malloc(strlen(name) + 1);
		strcpy(pName,name);
		mAge = age;
	}
	//增加拷贝构造函数
	Person(const Person& person){
		pName = (char*)malloc(strlen(person.pName) + 1);
		strcpy(pName, person.pName);
		mAge = person.mAge;
	}
	~Person(){
		if (pName != NULL){
			free(pName);
		}
	}
private:
	char* pName;
	int mAge;
};

void test(){
	Person p1("Edward",30);
	//用对象p1初始化对象p2,调用c++提供的默认拷贝构造函数
	Person p2 = p1;
}

 

六、初始化列表

注意:初始化成员列表(参数列表)只能在构造函数使用。

例子:

class Person{
public:
#if 0
	//传统方式初始化
	Person(int a,int b,int c){
		mA = a;
		mB = b;
		mC = c;
	}
#endif
	//初始化列表方式初始化
	Person(int a, int b, int c):mA(a),mB(b),mC(c){}
	void PrintPerson(){
		cout << "mA:" << mA << endl;
		cout << "mB:" << mB << endl;
		cout << "mC:" << mC << endl;
	}
private:
	int mA;
	int mB;
	int mC;
};

 

七、explicit关键字

[explicit注意]

  1. explicit用于修饰构造函数,防止隐式转化
  2. 是针对单参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造)而言
class MyString
{
public:

	explicit MyString(int len)
	{
		cout << "MyString有参构造函数(int  )调用" << endl;
	}

	MyString(char * str)
	{
		cout << "MyString有参构造函数(char * )调用" << endl;
	}

};

void test01()
{
	MyString str = "abcde";
	MyString str2("abcde");
	MyString str3 = MyString("abcde");

	//MyString str4 = 10; // 有人会认为是  字符串是  "10"  也有人会认为字符串长度为10
	//为了防止这种写法,可以用关键字 explicit

	MyString str5(10);

	MyString str6 = MyString(10);

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值