构造函数和拷贝构造函数和移动构造函数的区别和使用

文章目录

以C++中的类类型来说明

源码

cpp 复制代码
class Moveable
{

public:
	Moveable() : i(new int(3)){ //构造函数
		cout << "构造函数" << endl; 
	};
	~Moveable(){//析构函数
		delete i;//这里也没有必要判断i是否为空指针,然后再执行delete i,因为delete 一个指向空地址的指针变量 这里也不会报错,delete命令底层有优化。
		i = nullptr;//这里没必要把i置为空指针,因为i是对象中的非静态成员变量,对象生命周期结束后指针变量i生命周期也随着结束了。
	};
	Moveable(const Moveable & m) : i(new int(*m.i)){//拷贝构造函数
		cout << "调用拷贝构造函数" << endl;
	};
	Moveable(Moveable && m) : i(m.i){//移动构造函数
		cout << "调用移动构造函数" << endl;
		m.i = nullptr; 
	};
	int* i;
};

构造函数

构造函数,不显示的在类中写出来,会有隐式的构造函数。如:Moveable(){};什么参数都不需要传递。对象中的属性都是按照随机给值的。这样非常危险。所以一般都

会显示的重写构造函数,并在构造函数中初始化成员变量。构造函数支持重载。

什么时候会触发构造函数呢?

当创建对象时会自动触发调用构造函数。例如:Moveable m;//会触发调用构造函数

拷贝构造函数

拷贝构造函数,不显示的在类中写出来,会有隐式的拷贝构造函数。如:

Moveable(const Moveable & m) : i(m.i)){ };

拷贝何时会被触发调用呢。

Moveable m;//会触发调用构造函数

Moveable m1(m);//m是一个左值,不管编译器有没有做优化,都会调用拷贝构造函数

也就是根据一个已有的对象来生成另一个对象时,会触发拷贝构造函数。

浅拷贝

使用隐式拷贝构造函数,会产生浅拷贝问题,即新对象和旧对象中的指针成员 i都指向同一块堆内存,当这两个对象,其中一个释放内存时,会调一次delete i。

而另一个对象在释放内存时,也会调一次delete i。会产生同一个堆内存,被释放两次的情况,这种情况会使程序发生错误。

当第一次执行delete i 后,另一个对象中的指针变量i,就变成的悬空指针。也就是指针指向的内存已被释放了(不能再访问了)。

隐式拷贝构造函数就是 浅拷贝构造函数。为避免这种情况一般就需要重写拷贝构造函数。

深拷贝

针对以上隐式拷贝构造函数,带来的浅拷贝的问题。需要深拷贝来解决。

如:

Moveable(const Moveable & m) : i(new int(*m.i)){...拷贝操作...};

就是在拷贝的过程中,给新对象的指针成员属性重新分配一块堆内存,然后在 函数体内,把旧对象指针成员i指向的堆内存中的内容,拷贝一份到新对象指针成

员所指向的堆内存中。这样就解决了浅拷贝带来的悬空指针的问题。

移动构造函数

移动构造函数 是C++11中引入的一种新式构造函数,移动构造函数没有隐式的。想使用时,必须显示的在类中定义移动构造函数。

移动构造函数的形参类型是右值引用类型(也是C++11新出现的一个引用类型 例如: T &&)

例如:

Moveable(Moveable && m) : i(m.i){

m.i = nullptr;//移动语义

};

Moveable &&:就是一个右值引用类型,其实它在操作数据时,和 左值引用(Moveable &) 没啥区别,都表示要引用的对象的别名。

左值引用类型:用来引用左值的。

右值引用类型:用来引用右值的。

移动构造函数出现的意义是什么呢?

移动构造函数出现的意义就是在进行数据拷贝是,不想发生深拷贝那样的操作,如果指针成员i指向的那块堆内存内容特别大,那么进行深拷贝时,效率很低。而选择使用隐式拷贝构造函数 又会造成 上面所说的悬空指针的问题。

其实这里的移动构造函数可以看成是一个浅拷贝构造函数。因为重写了隐式的拷贝构造函数,那么类中就不在提供隐式的浅拷贝构造函数了。

所以在存在深拷贝构造函数的情况下,还想使用浅拷贝构造函数,又想避免浅拷贝带的悬空指针问题。那么移动构造函数就横空出世了。

移动构造函数和显示的深拷贝构造函数是可以共存的。

移动构造函数与默认的浅拷贝构造函数不同的地方,除了参数类型不一样外(一个是左值引用类型,一个是右值引用类型)。关键的区别在函数体内的移动语义。

移动构造函数和浅拷贝构造函数 都是在窃取别人的内容。

分析一下:移动构造函数的执行。首先接收一个右值,然后赋值给右值引用类型变量 m, 把旧对象指针i赋值给新对象的指针i。此时新对象的成员属性指针i也指向了旧对象的成员属性i所指向的那块堆内存。然后在函数体,把旧对象的属性指针i置为空指针。这样在旧对象生命周期结束时,触发析构函数时,就不会再释放堆内存了。所以也就避免了新对象属性指针i会变成悬空指针的问题。

一般情况传入移动构造函数中的那个右值,是将要生命周期结束的,或者说我们以后不会再使用到它了。但是还想要其对象里的内容(尤其是占大内存的对象)时。我们选择使用移动拷贝构造函数,效率高一些。

什么时候会触发移动构造函数呢?

Moveable m;

Moveable m2(std::move(m));//std::move(m)的唯一作用就是 将m强制转化为一个右值 。然后创建m2时调用的是移动构造函数。
注意:当类中没有定义移动构造函数时,这条语句Moveable m2(std::move(m));就会调用拷贝构造函数。

所以说是优先调用移动构造函数,没有的话,再调用拷贝构造函数。

std::move();//函数也是C++11中的新特性,作用就是把一个左值,转换成右值。

相关推荐
何曾参静谧25 分钟前
「C/C++」C/C++ 之 变量作用域详解
c语言·开发语言·c++
AI街潜水的八角35 分钟前
基于C++的决策树C4.5机器学习算法(不调包)
c++·算法·决策树·机器学习
JSU_曾是此间年少1 小时前
数据结构——线性表与链表
数据结构·c++·算法
此生只爱蛋2 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
何曾参静谧2 小时前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++
lulu_gh_yu3 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
ULTRA??4 小时前
C加加中的结构化绑定(解包,折叠展开)
开发语言·c++
凌云行者4 小时前
OpenGL入门005——使用Shader类管理着色器
c++·cmake·opengl
凌云行者4 小时前
OpenGL入门006——着色器在纹理混合中的应用
c++·cmake·opengl
~yY…s<#>5 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode