C++析构、拷贝、赋值、移动拷贝函数的几个知识点(不全)
怕忘了,写这:析构函数不会释放指针成员指向的对象(但智能指针类时有自己的析构函数的)。
析构函数中成员按初始化顺序的逆序销毁。内置类型没有析构函数。
继承体系中,派生类析构函数最先执行,然后是其基类的析构函数,以此类推,沿着继承体系的反方向往上。
众所周知,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++析构、拷贝、赋值、移动拷贝函数的几个知识点(不全)的更多相关文章
- 《Effective C++》阅读总结(二):类的构造、析构和赋值
今天是周六早上,但很不幸待会儿还是要去公司,本月kpi还剩一些工作要做,这个月计划的Effective C++学习,也基本完成了,最后一章节模板相关那部分还看不太懂,就大概过了一遍.现在是收尾总结阶段 ...
- 构造函数和初始化表、this指针与常函数、析构函数、拷贝构造与拷贝赋值(day05)
十四 构造函数和初始化表 ... 初始化表 )语法形式 class 类名{ 类名(形参表):成员变量1(初值),...{} }; )必须要使用初始化表的场景 -->如果有类 类型的成员变量,而该 ...
- c++不自动生成相关函数比如赋值、拷贝函数
默认情况下,如果没有明确声明某些函数比如赋值.拷贝函数,c++会自动生成这些函数,通常他们是对成员进行by-value拷贝,有些时候,赋值.拷贝对象并无什么意义或者不合理,比如对于socket或者th ...
- C++ class内的=重载,拷贝赋值函数copy op=,重载示例。必须是class内
#include <iostream> // overloading "operator = " inside class // = 是一元操作符.不写,编译器会提供 ...
- C++ Primer : 第十三章 : 拷贝控制之拷贝、赋值与销毁
拷贝构造函数 一个构造函数的第一个参数是自身类类型的引用,额外的参数(如果有)都有默认值,那么这个构造函数是拷贝构造函数.拷贝构造函数的第一个参数必须是一个引用类型. 合成的拷贝构造函数 在我们没 ...
- [c++基础]3/5原则--拷贝构造函数+拷贝赋值操作符
/* * main.cpp * * Created on: Apr 7, 2016 * Author: lizhen */ #include <iostream> #include &qu ...
- 拷贝构造和拷贝赋值、静态成员(static)、成员指针、操作符重载(day06)
十七 拷贝构造和拷贝赋值 浅拷贝和深拷贝 )如果一个类中包含指针形式的成员变量,缺省的拷贝构造函数只是复制了指针变量的本身,而没有复制指针所指向的内容,这种拷贝方式称为浅拷贝. )浅拷贝将导致不同对象 ...
- std::string的拷贝赋值研究
说明:以下涉及的std::string的源代码摘自4.8.2版本.结论:std::string的拷贝复制是基于引用计数的浅拷贝,因此它们指向相同的数据地址. // std::string类定义type ...
- 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 ...
随机推荐
- CSS selector? [class*=“span”]怎么理解
我在Twitter 中有看到如下selector: .show-grid [class*="span"] { background-color: #eee; text-align: ...
- GORM入门指南
gorm是一个使用Go语言编写的ORM框架.它文档齐全,对开发者友好,支持主流数据库. gorm介绍 Github GORM 中文官方网站内含十分齐全的中文文档,有了它你甚至不需要再继续向下阅读本文. ...
- Codeforces_739_B
http://codeforces.com/problemset/problem/739/B dfs,记录距离前缀和,每次找到离最近的不符合的点. #include<iostream> # ...
- HDU_1176_DP
http://acm.hdu.edu.cn/showproblem.php?pid=1176 简单dp,转换后跟上一题数塔一样,注意每秒只能移动一格,还有在边缘的情况. #include<ios ...
- Excel VBA: 自动生成巡检报表并通过邮件定时发送
目录 环境说明逻辑结构效果说明及截图①.安装SecureCRT②. 自动巡检脚本③. 数据检索并FTP传送④. 安装Excel 2013⑤. 安装Serv-U⑥. 自动生成图表并邮件发送 环境说明 系 ...
- Mysql索引优化简单介绍
一.关于MySQL联合索引 总结记录一下关于在MySQL中使用联合索引的注意事项. 如:索引包含表中每一行的last_name.first_name和dob列,即key(last_name, firs ...
- Hapi+MySql项目实战数据库操作(四)
数据库访问 下面以Node的ORM框架Sequelize来操作数据库,Mysql为例. 配置数据库连接信息config/db_config.js: //db_config.js module.expo ...
- php面试笔记(4)-php基础知识-流程控制
本文是根据慕课网Jason老师的课程进行的PHP面试知识点总结和升华,如有侵权请联系我进行删除,email:guoyugygy@163.com 在面试中,考官往往喜欢基础扎实的面试者,而流程控制相关的 ...
- [Redis-CentOS7]Redis事务操作(六)
事务操作 隔离操作: 事务中所有的命令都会序列化,按顺序执行,不会被其他命令打扰 原子操作: 事务中所有的命令要么全部执行,要么全部不执行 添加事务并执行 127.0.0.1:6379> MUL ...
- Class Literal(Java)
前言 上一节我们讨论过通过关键字synchronized实现线程同步,同时最主要了解到在Java中className.class所代表的具体含义,在博客写完后,感觉还是有点迷糊,然后再次深入了解后,原 ...