构造函数,拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符应用场景

#include <iostream>
using namespace std;class ConstructTest{
public:ConstructTest(){cout<<"default Constructor\n";dim_=0;base_= nullptr;};   ~ConstructTest(){cout<<"Destructor:base "<<base_<<endl;if (base_ != nullptr){delete base_;}}ConstructTest(int dim){cout<<"Constructor with param"<<endl;dim_=dim;base_ = new int [dim];for (int i = 0; i < dim_; ++i) {*(base_ + i) = 0;}}ConstructTest (const ConstructTest & a){cout<<"copy Constructor"<<endl;dim_= a.dim_;base_ = new int [dim_];for (int i = 0; i < dim_; ++i) {*(base_ + i) = *(a.base_+i);}}ConstructTest& operator =(const ConstructTest & a){cout<<"copy assign "<<endl;dim_= a.dim_;base_ = new int [dim_];for (int i = 0; i < dim_; ++i) {*(base_ + i) = *(a.base_+i);}return *this;}ConstructTest& operator =( ConstructTest && a)noexcept{cout<<"moving copy assign "<<endl;//避免自己移动自己if ( this == &a )return *this;delete base_;dim_ = a.dim_;base_ = a.base_;a.base_ = nullptr;return *this;}ConstructTest (ConstructTest && a) noexcept{cout<<"moving copy Constructor"<<endl;dim_ = a.dim_;base_ = a.base_;a.base_ = nullptr;}public:int  dim_;int * base_;
};
ConstructTest square(ConstructTest para){ConstructTest ret(para.dim_);ret.base_ = new int [para.dim_];for (int i = 0; i < para.dim_; ++i) {*(ret.base_+i) = *(para.base_+i) * (*(para.base_+i));}return  ret;
}int main(){ConstructTest c1(3);ConstructTest c2(c1);ConstructTest c4 ;c4=c2;cout<<"------------------------\n";ConstructTest c5 ;c5=square(c4);ConstructTest c6 = std::move(c5);cout<<"------------------------\n";ConstructTest c7;c7=ConstructTest();ConstructTest c8 = square(c7);ConstructTest c9 = ConstructTest();cout<<"<<<<<<finish >>>>>>>>\n";
}

C++ 和JAVA不一样的是,C++ 区分了值类型和引用类型,不像JAVA一样全是引用类型。创建对象 JAVA 用  <类名> 对象名= new ......  而C++ 用 <类名> 对象名 就行 . 对于普通对象 用值传递的方式传到形参,有一个隐式赋值的过程,此时调用的拷贝构造函数.以下的构造函数使用场景:

构造函数 : 创建对象时,给对象初始化时调用。

拷贝(复制)构造函数: 利用相同的类对象给新对象初始化.时调用.

拷贝赋值运算符 : 两个旧对象之间的赋值。

所谓“移动”就是把a的内存资源挪为自用。

移动构造函数: 在创建对象时,用临时对象初始化时调用。在返回值传给返回值的副本时也会调用。

移动赋值运算符: 用临时对象给旧对象赋值时调用。

我用的是CLION,在CMakeList.txt 加入如下代码,来取消编译器优化

add_compile_options(-fno-elide-constructors)

在无编译器优化的情况下,输出结果:

Constructor with param
copy Constructor
default Constructor
copy assign
------------------------
default Constructor
copy Constructor
Constructor with param
moving copy Constructor
Destructor:base 0
moving copy assign
Destructor:base 0
Destructor:base 0x3e1da8
------------------------
moving copy Constructor
------------------------
default Constructor
default Constructor
moving copy assign
Destructor:base 0
copy Constructor
Constructor with param
moving copy Constructor
Destructor:base 0
moving copy Constructor
Destructor:base 0
Destructor:base 0x3e1da8
default Constructor
moving copy Constructor
Destructor:base 0
<<<<<<finish >>>>>>>>
Destructor:base 0
Destructor:base 0x3e1918
Destructor:base 0
Destructor:base 0x3e1dd8
Destructor:base 0
Destructor:base 0x3e1d90
Destructor:base 0x3e1d78
Destructor:base 0x3e1d60Process finished with exit code 0

在main函数第一到第四行中,展示了构造函数,拷贝构造,和拷贝赋值。

从第5行起:

 cout<<"------------------------\n";ConstructTest c5 ;c5=square(c4);ConstructTest c6 = std::move(c5);cout<<"------------------------\n";

首先用 默认构造函数创建对象c5,

在c5 = square(c4) 中, 首先把实参c4赋值给square的形参param,此时用的是拷贝构造函数。

在square函数体中,创建了局部对象ret,此时调用构造函数,

然后为返回值ret创建副本,此时调用移动构造函数,接着函数体结束,把形参param析构,

然后把原来ret的副本赋值给c5 ,对过程是对已经存在的对象c5进行赋值,所以调用移动赋值。

接着把ret 和ret的副本给析构。

利用 std::move() 手动把c5的内容移动给某个临时引用,把临时引用初始化给c6,调用移动构造函数.

    cout<<"------------------------\n";ConstructTest c7;c7=ConstructTest();ConstructTest c8 = square(c7);ConstructTest c9 = ConstructTest();cout<<"<<<<<<finish >>>>>>>>\n";

先默认构造函数创建c7对象。

用默认构造函数创建临时对象,把临时对象"移动"给c7;移动完后把临时对象析构

在c8 = square(c7) 中, 首先把实参c7赋值给square的形参param,此时用的是拷贝构造函数。

在square函数体中,创建了局部对象ret,此时调用构造函数,

然后为返回值ret创建副本,此时调用移动构造函数,接着函数体结束,把形参param析构,

然后把原来ret的副本赋值给c8 ,对还未创建的对象的对象c8进行初始化,所以调用移动构造

接着把ret 和ret的副本给析构。

创建临时对象,并用给c9进行初始化,最后把临时对象析构掉;

程序结束,析构所有变量。

编程思路

在类成员变量带有指针的情况下,会面临如何施放资源的难题。应为默认拷贝构造,拷贝赋值,移动构造、移动赋值只是单单的浅拷贝。即值复制 指针指向内存的地址。在浅拷贝后,就会有两个对象中的成员指针指向同一处内存空间,如果在析构函数中对成员指针delete,最后就会面临对同一处内存空间施放两次。在C++中,如果访问不属于本程序中的内存就会出现“段错误”,即指针越界.

在编程时,可以利用 boost::shared_ptr 来管理动态分配的内存,此时可以不用理睬资源施放问题。因为智能指针能够对动态内存的引用计数归零时自动清理内存空间。

如果要用 普通指针来管理动态内存,那么就要考虑施放资源,浅拷贝与深拷贝的问题。如果想用深拷贝那么就要,写好构造函数,拷贝构造函数,拷贝赋值运算符,移动构造,移动赋值运算符;

在构造函数中初始化所有成员变量包括指针;在拷贝构造和拷贝赋值中重新为指针分配内存空间,并把对应的内存值进行赋值;在移动构造和移动赋值中,把返回内容复制完后要把临时对象指针内容重新赋空,达到不拷贝内存又不会施放两次内存的目的,即所谓的"移动“.

C++ : 构造函数,拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符应用场景相关推荐

  1. 拷贝构造,深度拷贝,关于delete和default相关的操作,explicit,类赋初值,构造函数和析构函数,成员函数和内联函数,关于内存存储,默认参数,静态函数和普通函数,const函数,友元

     1.拷贝构造 //拷贝构造的规则,有两种方式实现初始化. //1.一个是通过在后面:a(x),b(y)的方式实现初始化. //2.第二种初始化的方式是直接在构造方法里面实现初始化. 案例如下: ...

  2. C++远航之封装篇——默认构造函数、初始化列表、拷贝构造函数

    1.默认构造函数 没有参数: 若有参数,则一定全部都有默认的参数值. 2.初始化列表 (1)概念 (2)特性 建议用初始化列表来初始化数据成员. 初始化列表先于构造函数执行: 初始化列表只能用于构造函 ...

  3. c/c++教程 - 2.4.2.1~2 对象的初始化和清理,构造函数和析构函数,构造函数的分类和调用(有参构造,无参构造,普通构造,拷贝构造,括号法,显示法,隐式转换法,匿名对象)

    目录 4.2 对象的初始化和清理 4.2.1 构造函数和析构函数 4.2.2 构造函数的分类及调用 相关教程 4.2 对象的初始化和清理 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候 ...

  4. c++的构造函数极其调用(无参构造函数,有参构造函数,拷贝构造函数)

    1.c++编译器会自动调用构造函数 //构造函数(与类名相同) //析构函数:没有参数也没有任何返回类型,被自动调用 #include<iostream> using namespace ...

  5. 拷贝赋值函数、拷贝构造函数

    拷贝构造函数 也是构造函数的一种,常用来以另一对象为模板创建新对象.如果对象中没有指针,可以直接使用数据库自带的该函数,但有指针就需要自己构建,这是为了避免造成浅拷贝,使两个指针指向同一内存空间. 例 ...

  6. c构造函数和析构函数_C ++构造函数,析构函数能力问题和答案(第2组)

    c构造函数和析构函数 C ++构造函数和析构函数能力问题列表 (List of C++ Constructor and Destructor Aptitude Questions & Answ ...

  7. java反序列化 构造函数_FastJson反序列化和构造函数之间的一点小秘密

    各位看官大家下午好,FastJson想必大家都很熟悉了,很常见的Json序列化工具.今天在下要和大家分享一波FastJson反序列化和构造函数之间的一点小秘密. 下面先进入大家都深恶痛绝的做题环节.哈 ...

  8. java类中,成员变量赋值第一个进行,其次是静态构造函数,再次是构造函数

    如题是结论,如果有人问你Java类的成员初始化顺序和初始化块知识就这样回答他.下面是代码: package com.test;public class TestClass{// 成员变量赋值第一个执行 ...

  9. java的构造函数详解,Java构造函数与普通函数用法详解

    函数也被称为方法! 函数的作用及特点: 1.用于定义功能,将功能封装. 2.可以提高代码的复用性. 函数注意事项: 1.不能进行函数套用(不可以在函数内定义函数). 2.函数只有被调用才能被执行. 3 ...

  10. c构造函数和析构函数_C ++构造函数和析构函数| 查找输出程序| 套装1

    c构造函数和析构函数 Program 1: 程序1: #include <iostream> using namespace std; class Sample { private: in ...

最新文章

  1. pytorch问题索引
  2. AMP328音频放大器
  3. 4、数据类型二:Lists
  4. (5)DFS(深度优先搜索算法):排列数字
  5. zuul 自定义路由映射规则
  6. SRM 567 div2
  7. 50行python代码自动生成文章_如何通过50行Python代码获取公众号全部文章
  8. Oracle VM VirtualBox 无法卸载 更新 和修复
  9. Reflector 3在录制中如何添加自己的声音
  10. 雷允上药业百年老店回春
  11. 有水量服务器水温还是不稳定,我的热水器水量忽大忽小
  12. 《Python股票量化交易从入门到实践》随书赠送“回测框架”的使用帮助
  13. 用户名+密码控制+php文件存储
  14. 除夕之夜-梦游天姥呤留别
  15. 微信小程序底部弹窗(半屏弹窗)---WeUI组件使用
  16. 废旧HHD硬盘的使用-雕刻机/砂轮/3D打印机,超乎想象!
  17. 致远oa系统报价_用友致远OA 系统 一般多少钱?(公司60-70台左右的电脑)
  18. linux 卷标,linux 卷标设置与管理
  19. 商业大亨微信草花服务器,4399《商业大亨》跨服争夺,寸土寸金
  20. 宋宝华:世上最好的共享内存(Linux共享内存最透彻的一篇)上集

热门文章

  1. Redis--发布订阅模式
  2. iOS开发-面试总结(十四)
  3. JavaScript:Object.prototype.toString方法的原理
  4. Dreamweaver 8和visual studio 2003一起开发是很爽的事
  5. qt文件逐行读取_qt读取txt文件并绘图 qt逐行读取txt文件
  6. 【Paper】Network Dissection: Quantifying Interpretability of Deep Visual Representations跑源码遇到的问题
  7. HDFS HA模式下支持只连接其中Active的NameNode
  8. JavaFX Button和Scene点击事件代码示例
  9. 详解spark任务提交至yarn的集群和客户端模式
  10. Spring Boot 2.x整合Apollo代码示例