四.类和对象 -- 对象初始化和清理(4.2.3 -- 4.2.7)

本文深入探讨C++中的对象初始化和清理,涵盖拷贝构造函数的调用时机、构造函数的调用规则、深拷贝与浅拷贝的概念及其问题、构造函数初始化列表的使用以及类对象作为类成员的注意事项。文章通过实例代码解释了这些概念,并强调了初始化列表在提高效率和避免错误中的作用。
摘要由CSDN通过智能技术生成


前言


本文记录本渣渣依据黑马程序员C++课程视频学习C++笔记

本节记录C++中的类和对象 – 对象初始化和清理(4.2.3 – 4.2.7)


4.2.3 拷贝构造函数调用时机

C++中拷贝构造函数调用时机

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象

简单代码示例:

#include <iostream>

using namespace std;

class A_Class {

public:
	
	A_Class() {
		value = 0;
	}
	//默认构造函数

	A_Class(const A_Class& temp) {
		cout << "拷贝构造函数调用" << endl;
	}
	//拷贝构造函数

private:
	int value;
};

A_Class Func(A_Class temp) {//第二,第三种

	return temp;
}

void Fun(A_Class temp) {	//第二种

}

int main() {


	A_Class c1;
	A_Class c2(c1);			//使用一个已经创建完毕的对象来初始化一个新对象
	Fun(c2);				//值传递的方式给函数参数传值
	A_Class c3 = Func(c2);	//以值方式返回局部对象
							//实际上调用了4次


	system("pause");
	return 0;
}

运行结果:
运行结果


4.2.4 构造函数调用规则

默认情况下,C++编译器至少给一个类添加4个函数

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构寒素(无参,函数体为空)
  3. 默认拷贝构造函数,对成员属性进行值拷贝
  4. “ = ”运算符重载函数(之后将会详细展开,功能类似默认拷贝构造函数)

构造函数调用规则如下:

  • 如果用户定义了有参构造函数,C++将不再提供默认构造函数,但是会提供默认拷贝构造函数
  • 如果用户定义了拷贝构造函数,C++将不再提供其他构造函数

即:
定义有参,提供拷贝
定义拷贝,就无提供

我们可以通过简单代码证明:
第一条规则:
简单代码报错信息
第二条规则:
简单代码第二条规则第二条规则


4.2.5 深拷贝与浅拷贝

浅拷贝:简单但赋值操作(逐字节拷贝,也是默认拷贝方式)
深拷贝:在堆区重新申请空间,进行拷贝操作

浅拷贝简单代码示例:

class Myclass {

public:
	int getValue() {
		return *value;
	}

	Myclass() {
		value = new int(0);
	}

	Myclass(const Myclass& temp) {
		value = temp.value;			//直接赋值,为浅拷贝
	}

private:
	int* value;		//成员属性为int型指针
};

浅拷贝造成的问题

  • 浅拷贝,在析构对象时,容易执行重复释放操作,造成错误

图示(不怎么会画图,其中是temp,和,tem2个对象):
错误描述

当析构函数尝试释放tem.value 中值时,tem.value所指内存在temp销毁时已经释放了,系统回收后可能分配给其他程序使用,此时就会出错,所以在成员属性有指针时,我们应当尽量使用深拷贝

深拷贝简单代码示例:

class Myclass {

public:
	int getValue() {
		return *value;
	}

	Myclass() {
		value = new int(0);
	}

	Myclass(const Myclass& temp) {
		if (value != NULL) {				//判断value是否为空,不为空先释放,再赋值
			delete value;
			value = new int(*temp.value);
		}
		value = new int(*temp.value);		//在堆区重新申请空间,进行赋值
	}

private:
	int* value;		//成员属性int型指针
};

4.2.6 构造函数初始化列表

C++提供了初始化列表语法,用在构造函数初始化属性,
初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。

  • 语法:构造函数():成员属性(值), 成员属性(值){函数主体}

简单代码示例:

class Myclass {

public:
Myclass() :value(new int(0)) {} //构造函数列表初始化

private:
	int* value;		//成员属性int型指针
};

函数执行

  • 构造函数的执行可以分成两个阶段,初始化阶段和计算阶段
  • 先进行初始化,再进行计算

初始化阶段

  • 所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中.
  • C++初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。

计算阶段

一般用于执行构造函数体内的赋值操作。

简单代码示例:

#include <iostream>

using namespace std;


class myClass {

public:
	myClass():value(0) {		//列表初始化
		cout << "初始化value:" << value << endl;
		value = 100;			//计算
		cout << "计算value:" << value << endl;	
	}

private:
	int value;
};

int main()
{
	myClass test;

	system("pause");
	return 0;
}

运行结果:
运行结果

为何要使用列表初始化

  • 当A类对象作B类对象成员时,若B类构造函数使用了列表初始化,则B类中定义A类对象成员时,将不再调用A类构造函数
  • 对于内置类型int,float,double等类型来说,使用初始化列表和函数内初始化区别不大
  • 但是对于类来说:由于一个类有可能极其复杂,当成员非常多时,不使用列表初始化,将多调用一次构造函数,导致性能下降。此时使用列表初始化,是非常高效的。

由以下简单代码示例证明:

#include <iostream>

using namespace std;


class myClass {

public:
	myClass() :value(0) {
		cout << "myClass 默认(无参)构造函数" << endl;
	}					//列表初始化

	myClass(int temp) :value(temp){
		cout << "myClass 有参构造函数" << endl;
	}					//列表初始化

	myClass(const myClass& temp) :value(temp.value){
		cout << "myClass 拷贝构造函数" << endl;
	}					//列表初始化

private:
	int value;
 
};

class yourClass {

private:
	myClass mine;		//myclass类对象作yourClass 类成员
	myClass your;		//使用列表初始化,将不再调用类myClass默认构造函数

public:

	yourClass():mine(0), your(mine){}	//列表初始化

};

int main()
{
	yourClass test;

	system("pause");
	return 0;
}

运行结果:
运行结果
可以看到,只调用了2次函数,是由于类yourClass中初始化列表中

yourClass():mine(0), your(mine){}	//列表初始化

调用了类myClass中的有参和拷贝构造函数,而定义时,未调用无参(默认)构造函数

注意事项

  • C++初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。所以在编写列表初始化时,应当按成员出现顺序书写
  • 在类成员组成十分复杂时,应尽量使用初始化列表

4.2.7 类对象作为类成员

上面以有提到,C++一个类中的成员可以是另一个类的对象,我们称该成员为对象成员
简单代码示例:

class yourClass {

private:
	myClass mine;		//myclass类对象作yourClass 类成员
	myClass your;		

public:
};

注意事项

  • 当其他类对象作为本类成员是,构造时先构造类对象成员,再构造自身
  • 析构的顺序与构造相反,即析构自身,再析构类对象成员

简单代码示例:

#include <iostream>

using namespace std;


class myClass {

public:
	myClass() {
		cout << "myClass 构造函数" << endl;
	}

	~myClass() {
		cout << "myClass 析构函数" << endl;
	}

private:
	int value;
 
};

class yourClass {


public:
	yourClass() {
		cout << "yourClass 构造函数" << endl;
	}
	~yourClass() {
		cout << "yourClass 析构函数" << endl;
	}


private:
	myClass mine;		//myclass类对象作yourClass 类成员


};

void test() {
	yourClass test;

}

int main()
{
	test();//函数结束,对象销毁,执行析构函数

	system("pause");
	return 0;
}

运行结果:
运行结果


总结

  • 了解构造函数细则
  • 了解深拷贝与浅拷贝问题
  • 了解构造函数列表初始化细则
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值