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

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

定义行为像值的类

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. Linux上天之路(十)之Linux磁盘管理

    主要内容 磁盘介绍 磁盘管理 磁盘限额 逻辑卷管理 磁盘阵列 1. 磁盘介绍 硬盘最基本的组成部分是由坚硬金属材料制成的涂以磁性介质的盘片,不同容量硬盘的盘片数不等.每个盘片有两面,都可记录信息.盘片 ...

  2. Go语言系列之标准库path/filepath

    一.Path包 import "path" path实现了对斜杠分隔的路径进行操作的函数. func IsAbs(path string) bool // 判断是否是一个绝对路径 ...

  3. Word2010制作简单个人简历

    原文链接:https://www.toutiao.com/i6489366535050625550/ 以学习使用按钮属性为主,具体样式可以根据个人设置 选择"页面布局"选项卡,&q ...

  4. Git 基础指令

    Git 基础指令 Git 基础指令 获取 Git 仓库 在已存在目录中初始化仓库 克隆现有的仓库 记录仓库与仓库的更新 仓库的记录 检查当前文件状态 三部曲 跟踪新文件 提交更新 移除文件 推送到远程 ...

  5. kafka学习笔记(六)kafka的controller模块

    概述 今天我们主要看一下kafka的controller的代码,controller代码是kafka的非常重要的代码,需要我们深入学习.从某种意义上来说,它是kafka最核心的组件,一方面,他要为集群 ...

  6. Python向mysql数据库插入数据

    一.向表tcolor中插入数据的主要流程如下: import datetimeimport pymysql.cursorsconnection = pymysql.connect(host='loca ...

  7. X架构键盘选购指南

    X架构键盘 联想 JME8002(未知) JME7597(推荐) 据说是ThinkPAD键盘的血统. Thinkapad 小红点键盘,据说腿撑容易坏 明基 明基精钢侠 海湾海贝(键程长,不累手,但不易 ...

  8. 集合框架-ArrayList集合存储自定义对象

    1 package cn.itcast.p3.arraylist.test; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; ...

  9. mongo用户认证

    mongo@rayos:/opt/mongodb$ mongo --port 28017 MongoDB shell version v4.0.13 connecting to: mongodb:// ...

  10. jenkins自动化pipline的ci/cd流水线

    pipeline { agent any tools { //工具必须预先在jenkins中预配置 maven 'mvn' jdk 'jdk' } stages { stage('Env') { st ...