拷贝构造函数、拷贝运算符、析构函数

拷贝构造函数、拷贝运算符、析构函数

定义行为像值的类

class HasPtr{
public:
HasPtr(const string &s = string()):ps(new string(s)), i(0) {
cout<<"HasPtr(const string &s = string())"<<endl;
}
HasPtr(const HasPtr &hp) :ps(new string(*hp.ps)), i(hp.i) {
cout<<"HasPtr(const HasPtr &hp)"<<endl;
}
HasPtr &operator=(const HasPtr &hp) {
auto tmp = new string(*hp.ps); // 右侧对象拷贝到临时变量,考虑自己拷贝自己的情况。
       delete ps; // 析构左侧对象
       ps = tmp;
i = hp.i;
cout<<"HasPtr &operator="<<endl;
return *this;
} HasPtr testCopy(HasPtr hp) {
return hp;
} HasPtr &testCopy1(HasPtr &hp) {
return hp;
} ~HasPtr() {
cout<<"~HasPtr()"<<endl;
} void print() {
cout<<*ps<<endl;
} void setPs(const string &str) {
*ps = str;
} private:
string *ps;
int i;
};

测试代码

HasPtr hp("hello");                // 调用构造函数,输出HasPtr(const string &s = string())
HasPtr hp1 = hp; // 调用拷贝构造函数,输出HasPtr(const HasPtr &hp)
HasPtr hp2; // 调用构造函数,输出HasPtr(const string &s = string())
hp2 = hp; // 调用拷贝运算符函数,输出HasPtr &operator=
hp1.setPs("word"); // 构造函数、拷贝构造函数、拷贝赋值运算符函数都为ps分配了新空间
hp.print(); // 所以hp、hp1、hp2的ps指针指向的空间不一样,修改一个的值其他的不变。
hp1.print(); // 输出word,由setPs函数修改
hp2.print(); // 输出hello,值从hp拷贝过来,空间是新分配的。
// 实参初始化形参要调用一次拷贝构造函数,形参初始化返回的临时变量也要调用一次拷贝构造函数,
hp.testCopy(hp1); // 形参和临时变量在函数结束时时要销毁,调用两次析构函数。
hp.testCopy1(hp1); // 都是引用初始化,不需要调用拷贝构造函数。
HasPtr *hp_ptr = new HasPtr; // 调用构造函数
vector<HasPtr> vec; // 调用vector的默认构造函数。
vec.push_back(hp); // 调用拷贝构造函数 // 接下来要销毁hp,hp1,hp2和vector中的HasPtr的元素,要调用4次析构函数,但是hp_ptr指向的空间不会被销毁。

定义行为像指针的类

class HasPtr{
public:
// 构造函数,为指针指向分配空间(整个类也只在这个地方分配空间),use初始化为1
HasPtr(const string &s = string()):ps(new string(s)), i(0) ,use(new size_t(1)){
cout<<"HasPtr(const string &s = string())-->use"<<*use<<endl;
} // 拷贝构造函数,只拷贝指针,use值递增
HasPtr(const HasPtr &hp) :ps(new string(*hp.ps)), i(hp.i) ,use(hp.use){
++*use;
cout<<"HasPtr(const HasPtr &hp)-->use"<<*use<<endl;
} // 拷贝赋值运算符,左侧use递减,当use为0,释放左侧指针指向空间,拷贝右侧数据和指针到左侧对象,use递增。
HasPtr &operator=(const HasPtr &hp) {
if(--*use == 0) {
delete ps;
delete use;
}
++*hp.use;
ps = hp.ps;
use = hp.use;
i = hp.i;
cout<<"HasPtr &operator=-->use"<<*use<<endl;
return *this;
} HasPtr testCopy(HasPtr hp) {
return hp;
} HasPtr &testCopy1(HasPtr &hp) {
return hp;
} // 析构函数,递减use,当use为0,释放指针指向空间。
~HasPtr() {
if(--*use == 0){
delete ps;
delete use;
}
cout<<"~HasPtr()-->use"<<*use<<endl;
} void print() {
cout<<*ps<<endl;
} void setPs(const string &str) {
*ps = str;
} private:
string *ps; // 共享,只有构造函数分配,最后一次析构函数销毁
int i; // 每个对象独享,每次构造函数、拷贝构造函数、拷贝运算符都分配,每次析构都销毁
size_t *use; // 共享,只有构造函数分配,最后一次析构函数销毁
};

测试程序

HasPtr hp("hello");                // 调用构造函数,输出HasPtr(const string &s = string())
HasPtr hp1 = hp; // 调用拷贝构造函数,输出HasPtr(const HasPtr &hp)
HasPtr hp2; // 调用构造函数,输出HasPtr(const string &s = string())
hp2 = hp; // 调用拷贝运算符函数,输出HasPtr &operator=
hp1.setPs("word"); // 拷贝构造函数、拷贝赋值运算符不为指针ps和use分配新空间
hp.print(); // 所以hp、hp1、hp2的ps指针指向相同的空间,修改一个的值其他的也变。
hp1.print(); // 输出word,由setPs函数修改
hp2.print(); // 输出word,由setPs函数修改
// 实参初始化形参要调用一次拷贝构造函数,形参初始化返回的临时变量也要调用一次拷贝构造函数,
hp.testCopy(hp1); // 形参和临时变量在函数结束时时要销毁,调用两次析构函数。
hp.testCopy1(hp1); // 都是引用初始化,不需要调用拷贝构造函数。
HasPtr *hp_ptr = new HasPtr; // 调用构造函数
vector<HasPtr> vec; // 调用vector的默认构造函数。
vec.push_back(hp); // 调用拷贝构造函数 // 接下来要销毁hp,hp1,hp2和vector中的HasPtr的元素,要调用4次析构函数,只有最后一次调用析构函数才会释放ps和use指向的空间。
// 但是hp_ptr指向的空间不会被销毁。

c++ 拷贝构造函数、拷贝运算符、析构函数的更多相关文章

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

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

  2. 拷贝构造函数 & 拷贝赋值运算符

    一.拷贝构造函数 1. 形式 class A { public: // ... A(const A &); // 拷贝构造函数 }; 2. 合成拷贝构造函数 编译器总会为我们合成一个拷贝构造函 ...

  3. C++ 类 & 对象-类成员函数-类访问修饰符-C++ 友元函数-构造函数 & 析构函数-C++ 拷贝构造函数

    C++ 类成员函数 成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义. 需要强调一点,在 :: 运算符之前必须使用类名.调用成员函数是在对象上使用点运算符(.),这样它就能操作与 ...

  4. 类string的构造函数、拷贝构造函数和析构函数

    原文:http://www.cnblogs.com/Laokong-ServiceStation/archive/2011/04/19/2020402.html   类string的构造函数.拷贝构造 ...

  5. 编写类String的构造函数、拷贝构造函数、析构函数和赋值函数

    一.题目: class String { public: String(const char *str = NULL); // 普通构造函数 String(const String &othe ...

  6. C++基础 (3) 第三天 构造函数 构造函数初始化列表 拷贝构造函数 析构函数 静态成员变量

    // 同类之间无私处 2构造函数 3析构函数 4构造函数的种类和析构函数的顺序 结论:析构函数的调用顺序,跟对象的构造顺序相反,谁先构造,谁最后一个被析构. 拷贝构造函数: 注意: 等号写在下面和写在 ...

  7. C++的转换构造函数、拷贝构造函数、赋值运算符重载

    1 转换构造函数     C++的转换构造函数是只有一个参数的构造函数.当程序试图将一个其他类型的对象或基本类型值赋给该类的一个待初始化对象时(如Person p="Dean";) ...

  8. C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数

    构造函数 ,是一种特殊的方法 .主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 .特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数 ...

  9. 对C++拷贝构造函数的一点理解

    一. 什么是拷贝构造函数 先看一个简单的例子: #include <iostream> using namespace std; class CExample { private: int ...

  10. 如何避免被C++默认拷贝构造函数忽悠?

    一.背景介绍           因为工作关系,需要用到C++编程.对于我来说,虽然一直从事的是linux平台下的嵌入式软件开发,但深入用到C++的特性的地方并不多.对于C++,用得最多的无非是指针. ...

随机推荐

  1. Go语言系列之标准库os

    os包提供了操作系统的系列函数,这些接口不依赖平台.设计为Unix风格的,错误处理是go风格的:调用失败会返回错误值而非错误码.通常错误值里包含更多信息. os包的接口在所有操作系统中都是一致的.非公 ...

  2. 微软开源Kubernetes服务网格项目Open Service Mesh​

    尽管微服务环境提供可移植性,允许更快更频繁的部署周期,甚至还能让组织创建关注于特定领域的团队,但这也伴随着对于流量管理.安全以及可观测性等需求的增长.在整个生态系统中,针对这些需求的服务网格模式的实现 ...

  3. 金融云原生漫谈(三)|银行云原生基础设施构建:裸金属VS虚拟机

    在金融行业数字化转型的驱动下,国有银行.股份制银行和各级商业银行也纷纷步入容器化的进程.   如果以容器云上生产为目标,那么整个容器云平台的设计.建设和优化对于银行来说是一个巨大的挑战.如何更好地利用 ...

  4. EF4中多表关联查询Include的写法

    大家好,好久没有写作了,最近遇到了个问题,最终是靠自己的尝试写出来的,希望可以帮到有需要的人. 在我们查询时通常会遇到多级表关联的情况,很多时候有人会想写一个from LINQ语句来解决,那么冗长的代 ...

  5. sql语句操作(详细)

    基础部分 查: 查询 SELECT * FROM `veteran_user` /查询表中所有数据 SELECT DISTINCT "name" from veteran SELE ...

  6. SQL查询中关键字的执行顺序

    SQL语句中的每个关键字都按照顺序往下执行,而每一步操作会生成一个临时表,最后的临时表就是最终结果: FROM <left_table>:from子句返回初始结果集 <join_ty ...

  7. 简述ASP.NET网站开发步骤

    新建解决方案 清除解决方案 重新生成解决方案 发布应用程序 设置配置文件 重命名配置文件 发布后生成的文件 IIS安装 安装完成后,输入http://localhost/出现 打开IIS 添加自己的网 ...

  8. 使用EdgyGeo Cesium工具查询下载数据集

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ EdgyGeo, Inc. 带来了一件非常棒的工作支撑架构.工程 ...

  9. npm 和 yarn 前端包管理工具

    前言 前端开发逐渐工程化,npm作为我们的依赖管理工具起到十分重要的作用,本文就来总结一下 npm 和 yarn 相关知识点. 正文 1.什么是npm (1)node的包管理器(node packag ...

  10. golang中数组指针与指针数组的区别实现

      指针数组和数组的指针,指的是两个不同的东西. 指针数组是有指针组成的数组,数组的指针是一个数组的指针. package main import "fmt" const MAX ...