C++拷贝控制成员

拷贝控制成员

C++中的拷贝控制成员包括:

  1. 构造函数
  2. 拷贝构造函数
  3. 拷贝赋值运算符
  4. 移动构造函数
  5. 移动赋值运算符
  6. 析构函数

构造函数

构造函数,又分为默认构造函数和自定义构造函数,其中默认构造函数包括:

  1. 系统自动合成(没有构造函数时)
  2. 无参构造函数
  3. 全部带有默认实参的构造函数

关于构造函数,这里重点介绍一下隐式类类型转换和显示的类类型转换

如果一个构造函数只含有一个参数例如int a,则该函数可以通过隐式转换将int类型的遍历转换为对象A。、

eg:

class A
{
    public:
    	A(int a):a_(a){} //构造函数1
    	int a_;
    	int b_;
}
int a;
A t = a;//将调用构造函数1
void func(A t);
func(a);//将调用构造函数1
int b;
t=b;//将先调用构造函数1将b隐式转换为A的对象,然后调用A的拷贝赋值运算符

隐式类型转换发生的地方包括:

  1. 通过a初始化A对象时
  2. 通过a作为实参传入A的形参时
  3. 为A对象赋值为a时(此时先将a隐式转换为A对象,然后调用拷贝赋值运算符)

注意:隐式类型转换只允许一步到位,不允许a先转换为b,然后b再转换为A的对象

禁止隐式类型转换的方法,是在构造函数1的声明(只需要在类的声明后加explicit,类外定义不加)后加explicit关键字即可

但是这样还是可以通过static_cast<A>(a)等显示的强制类型转换符来进行a->A对象的强制类型转换

拷贝和赋值

A(const A& a);
A& operator=(const A& a);

为什么拷贝和赋值的形参是引用?

这里首先需要知道拷贝构造函数被调用的三个条件

  1. 将一个对象作为实参传递给一个非引用类型的形参
  2. 从一个返回类型为非引用类型的函数返回一个对象
  3. 用花括号初始化一个数组中的元素或者一个聚合类中的成员
  4. 拷贝/直接初始化一个类对象

所以很显然,如果不是引用,将会导致循环再次调用拷贝构造函数,从而出错

深拷贝和浅拷贝

  1. 深拷贝是指每个对象之间独立,拥有独立的内存空间
  2. 浅拷贝是指每个对象中有成员共享相同的内存空间

深拷贝和浅拷贝常见于对含有指针成员的类对象而言,如果是这样一般需要进行深拷贝,或者如果进行浅拷贝则需要在类中添加引用计数(或者通过智能指针来解决)

注意拷贝赋值运算符完成的是拷贝构造函数和析构函数的事

析构函数

当一个类对象结束其生命周期,则调用析构函数释放类资源

析构函数释放资源的操作发生在析构函数体执行完以后,这和构造函数恰好相反(构造函数是先初始化然后执行构造函数体)

拷贝控制成员的三/五法则

  1. 需要析构函数的类一定需要拷贝和赋值
  2. 需要拷贝/赋值的类一定需要赋值/拷贝

default和delete

default的使用方法是在类内函数声明或者类外函数定义处写出A()=defualt;即可(不可以两个都写)

delete的使用方法必须放在类内函数声明后面

default的作用是希望编译器为类生成合成的拷贝控制成员

delete的作用是阻止类生成某种拷贝控制成员

  1. 可以通过delete来阻止类的拷贝和赋值
  2. 析构函数不能定义为delete
    • 如果析构函数是delete/某个类中有个类成员的析构函数是delete,则该类不能创建对象
    • 可以通过动态分配的方式创建对象,但是不能delete 该指针
  3. 合成的拷贝控制成员也可能因为某些原因被定义为delete(P450)

也可以将拷贝控制成员声明成private,然后不定义即可,那么在类外使用时将编译报错(不能访问private成员),类内则将链接报错(因为没有定义该函数,所以链接报错)

移动和右值

这一块是C++11中引入的一个常考的点,分为三点来描述这里,分别是左值和右值 左值引用和右值引用 移动构造函数

左值和右值

左值:可以放在赋值符号左边的变量,即具有对应的可以由用户访问的存储单元,并且能够由用户去改变其值的量,左值就是存储在计算机内存的对象,而不是常量或者计算的结果,左值代表的是内存地址的值

右值:指的是临时性的对象的表达式,或者字面常量

  1. 左值可以取地址,右值不行
  2. 使用左值时,用的是对象的地址,而使用右值时则是使用的是存储在内存中某些地址中的数值

C++11中又将右值分为纯右值和将亡值

  1. 纯右值,指临时的对象或者字面常量
  2. 将亡值,指跟右值引用相关的表达式,这样的表达式通常是将要被移动的对象,例如返回右值引用T&&的函数返回值、std::move的返回值等

何时产生临时对象?

  1. 类型转换时
  2. 函数值传参时
  3. 表达式求值时
左值引用和右值引用

左值引用就是对左值进行引用的类型,右值引用就是对一个右值进行引用的类型,两者都属于引用类型,无论是声明一个左值引用还是右值引用,都必须立即进行初始化。左值引用是变量的别名

  1. 普通的左值引用不能绑定到右值
  2. 常量左值引用可以绑定到非常量左值,常量左值,右值,但是常量左值引用则只能读。
  3. 右值引用通常不能绑定到任何左值,但是可以通过std::move()函数将左值强制转化为右值
移动构造函数与移动拷贝运算符

所谓移动操作,实际上就是窃取资源,其不会分配任何资源,而是接管已经分配的内存(资源)

以类A为例,如果用a去初始化b,或者用a去赋值b,同时在初始化或者赋值后,b不再使用,则可以用移动构造函数/移动拷贝运算符,这两个函数的作用是用b去接管a中成员的内存空间,同时将a中的指针置空。

如果一个类定义了自己的拷贝控制成员(拷贝/移动/析构函数),那么编译器不会为该类合成两个移动函数,除非没有定义,所以如果没有移动构造函数,那么如果是一个右值那么也会调用拷贝构造函数,通过拷贝来构造

C++成员函数的引用限定符

&用于限定该函数返回的是一个左值

&&用于限定该函数返回的是一个右值

&/&&必须同时用于声明和定义后,且放在const后

发布了23 篇原创文章 · 获赞 4 · 访问量 2117

猜你喜欢

转载自blog.csdn.net/hdadiao/article/details/104784505
今日推荐