怕忘了,写这:析构函数不会释放指针成员指向的对象(但智能指针类时有自己的析构函数的)。

析构函数中成员按初始化顺序的逆序销毁。内置类型没有析构函数。

继承体系中,派生类析构函数最先执行,然后是其基类的析构函数,以此类推,沿着继承体系的反方向往上。


众所周知,C++的类如果没有默认构造函数,会自动生成一个。

同理,如果没有复制构造函数即A::A(const A&){}这个函数 ,则系统也会自动生成一个,但这个自动生成的复制构造函数不一定满足我们的要求。析构函数也会自动生成(如果没定义的话)。

比如下面的例子:

 class A{
public:
int* a;
int b;
A()=default;
A(int x):a(new int()){b=x;}
~A(){delete a;cout<<"我删除a了!"<<endl;}
};

其中我们定义了默认构造函数、另一个重载版本的构造函数。但是我们没有定义复制构造函数,所以系统自动帮我们生成了一个,作用大致可以理解为下面的函数:

A(const A& another){
a=another.a;
b=another.b;
}

我们也没有定义析构函数,系统也自动生成了一个,类似下面:

~ A(){
  delete a;
  delete b;
}

而我们注意到类A中有一个指针成员,那么在默认的拷贝构造函数中就会简单的复制指针到另一个A变量。如A x1(x2); x1和

x2的指针成员a是一样的。这根本不是我们的本意,我们的本意是在上面代码第5行:每个A变量的成员a应该初始化为一个新建int变量的地址,而不同A变量之间的成员a应该是不同的。

所以可能出现的问题就是:如果x2空间被释放了,x1的成员a也就无效了,其指向的值是未定义的。。

或者可能有另一个函数这样定义,更好理解:

void f(A temp)
{
//.......
}

那么调用f(x1)的时候,会先调用拷贝构造函数,复制一个x1的副本作为形参。然后这个副本temp就拥有了和x1一样的成员a,当退出函数f的时候,temp的成员a被析构释放,这导致x1的成员a也变成了野指针。

我们自己定义好正确的拷贝构造函数即可解决上面的问题。

所以,遇到类的成员有非普通类型的时候(如指针),就一定要自己写拷贝构造函数、重载赋值符、移动构造函数、重载移动赋值符、析构函数。

注意:如果只定义了移动构造函数 or 重载移动赋值符,那么编译器是不会自动帮你生成拷贝构造函数和重载赋值符的,而是会默认定义为删除的(=delete;)。


下面看下各种构造函数和拷贝函数,加深下印象。

要注意的是如果我们有A x1;

A x2=x1;和 A x3;x3=x1;是不一样的阿!

前者是声明时就初始化,属于拷贝初始化。调用的是拷贝构造函数( A& (const A& another){ }

后者是先声明,默认初始化。然后赋值。先调用默认构造函数(A( ) {  }),再调用重载赋值符,即( A& operator=(const A& a) )

 class A
{
private:
int x;
public:
A(){cout<<"A()"<<endl;} //默认构造函数
A(int&& a){x=a;cout<<"A(int&& a)"<<endl;} //重载的构造函数
A(A&& a){cout<<"A(A&& a)"<<endl;x=a.x;} //移动拷贝函数
A(A& a){cout<<"A(A& a)"<<endl;x=a.x;} //拷贝构造函数
A& operator=(const A&& a){if(this!=&a){x=a.x;}cout<<"A& operator=(A&&)"<<endl;} //移动赋值符
A& operator=(const A& a){if(this!=&a){x=a.x;}cout<<"A& operator=(A&)"<<endl;} //拷贝赋值符
~A(){cout<<"删除了!"<<endl;} //析构函数
};
int main()
{ int a=;
A x1;
A x2();
A x3=move(x2);
A x4=x3;
x1=move(x2);
x2=x3;
getchar();
return ;
}

输出:


另外一个知识点,好像之前看剑指offer也看过来着,当时印象不深:

编写类的赋值运算符重载时,几个要求:
1.自赋值能正常运行不报错。
2.赋值运算符一般都集合了复制构造函数和析构函数二者的功能。

3.不要先删数据,再拷贝新数据到this的空间!这样容易删完了自己的,但拷贝又异常失败了,那该实例原来的数据就没得了

例子:

 A& operator=(const A& x){
if(this!=&x){
A temp(x);
a=temp.a;
b=temp.b;
//......//
}
return *this;
}

这样的目的是避免在函数中new空间时抛异常,会导致之前实例的数据变化。上面代码中的临时变量A如果申请失败,函数直接退出,不会影响原先该实例的数据。

先建立一个临时变量,然后依次赋值成员变量的值到this的成员,最后返回当前实例的引用,这样函数退出时temp也被自动析构释放。当然这个例子是建立在已经写好复制构造函数和析构函数的前提下,否则这个函数中 程序员应该自己写好对应的功能。

C++析构、拷贝、赋值、移动拷贝函数的几个知识点(不全)的更多相关文章

  1. 《Effective C++》阅读总结(二):类的构造、析构和赋值

    今天是周六早上,但很不幸待会儿还是要去公司,本月kpi还剩一些工作要做,这个月计划的Effective C++学习,也基本完成了,最后一章节模板相关那部分还看不太懂,就大概过了一遍.现在是收尾总结阶段 ...

  2. 构造函数和初始化表、this指针与常函数、析构函数、拷贝构造与拷贝赋值(day05)

    十四 构造函数和初始化表 ... 初始化表 )语法形式 class 类名{ 类名(形参表):成员变量1(初值),...{} }; )必须要使用初始化表的场景 -->如果有类 类型的成员变量,而该 ...

  3. c++不自动生成相关函数比如赋值、拷贝函数

    默认情况下,如果没有明确声明某些函数比如赋值.拷贝函数,c++会自动生成这些函数,通常他们是对成员进行by-value拷贝,有些时候,赋值.拷贝对象并无什么意义或者不合理,比如对于socket或者th ...

  4. C++ class内的=重载,拷贝赋值函数copy op=,重载示例。必须是class内

    #include <iostream> // overloading "operator = " inside class // = 是一元操作符.不写,编译器会提供 ...

  5. C++ Primer : 第十三章 : 拷贝控制之拷贝、赋值与销毁

    拷贝构造函数 一个构造函数的第一个参数是自身类类型的引用,额外的参数(如果有)都有默认值,那么这个构造函数是拷贝构造函数.拷贝构造函数的第一个参数必须是一个引用类型. 合成的拷贝构造函数   在我们没 ...

  6. [c++基础]3/5原则--拷贝构造函数+拷贝赋值操作符

    /* * main.cpp * * Created on: Apr 7, 2016 * Author: lizhen */ #include <iostream> #include &qu ...

  7. 拷贝构造和拷贝赋值、静态成员(static)、成员指针、操作符重载(day06)

    十七 拷贝构造和拷贝赋值 浅拷贝和深拷贝 )如果一个类中包含指针形式的成员变量,缺省的拷贝构造函数只是复制了指针变量的本身,而没有复制指针所指向的内容,这种拷贝方式称为浅拷贝. )浅拷贝将导致不同对象 ...

  8. std::string的拷贝赋值研究

    说明:以下涉及的std::string的源代码摘自4.8.2版本.结论:std::string的拷贝复制是基于引用计数的浅拷贝,因此它们指向相同的数据地址. // std::string类定义type ...

  9. python中的赋值与拷贝(浅拷贝与深拷贝)

    1.赋值与拷贝 直接赋值(b=a)是传引用,b改动a也会改动. a = [1, 2, 3, 4] b = a b[1] = 999 print(a, b) #[1, 999, 3, 4] [1, 99 ...

随机推荐

  1. HDU_5057_分块

    http://acm.hdu.edu.cn/showproblem.php?pid=5057 分块,保存每个块中每位对应数字的和,复杂的是getmum,左右下标所在的块不能直接读取block数组,要重 ...

  2. ArrayList 并发操作 ConcurrentModificationException 异常

    1.故障现象 ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常 2.故障代码 public class ...

  3. 关于将笔记本电脑作为wifi热点的详细步骤

    常规做法直接度娘.如果出现无法打开wifi功能,可找对应解决方法. 1.先检查网卡是否支持承载网络,检查方法为在cmd中使用管理员权限运行,输入netsh wlan show drivers.查看支持 ...

  4. [源码分析] 从源码入手看 Flink Watermark 之传播过程

    [源码分析] 从源码入手看 Flink Watermark 之传播过程 0x00 摘要 本文将通过源码分析,带领大家熟悉Flink Watermark 之传播过程,顺便也可以对Flink整体逻辑有一个 ...

  5. 数据算法 --hadoop/spark数据处理技巧 --(3.左外连接 4.反转排序)

    三. 左外连接 考虑一家公司,比如亚马逊,它拥有超过2亿的用户,每天要完成数亿次交易.假设我们有两类数据,用户和交易: users(user_id,location_id) transactions( ...

  6. 如何使用Xcode调试Shader代码Bug导致的渲染问题

    我最近发现了一个与Unity中的表面着色器有关的小Bug. 你可以看到如下所示的渲染瑕疵. 有时人们会将相似的渲染瑕疵归因于同时使用HDR和Bloom效果,但实际上,表面着色器是错误的,至少在本文中所 ...

  7. REDTEAM 指南---第四章 外部侦察

    第四章 外部侦察 贡献者:Haythem Arfaoui 翻译BugMan 主动侦察 介绍 主动足迹涉及使用可以帮助您收集更多信息的工具和技术 有关目标的信息.与被动足迹不同的是,过程永远不会“触及” ...

  8. ORB-SLAM2 论文&代码学习 —— LoopClosing 线程

    转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12369339.html 本文要点: ORB-SLAM2 LoopC ...

  9. 子组件获取父组件数据 propsDown, 父组件获取子组件数据 eventUp

    (一) popsDowm 三种方法获取父组件数据:被动获得(1):主动获取(2). 1.被动获得: 父组件:v-bind: 绑定变量参数和方法参数:子组件:props 接收参数.可以在模板中直接使用也 ...

  10. Caliburn.Micro框架之Action Convertions

    首先新建一个项目,名称叫Caliburn.Micro.ActionConvertions 然后删掉MainWindow.xaml 然后去app.xaml删掉StartupUri这行代码 其次,安装Ca ...