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

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

定义行为像值的类

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. Mybatis-Plus的引用

    一.依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-b ...

  2. 简单Spring MVC项目搭建

    1.新建Project 开发环境我使用的是IDEA,其实使用什么都是大同小异的,关键是自己用的顺手. 首先,左上角File→New→Project.在Project页面选择Maven,然后勾上图中所示 ...

  3. 读《疯狂Java讲义》笔记总结一

    最近在读<疯狂Java讲义>,现把其中遇到的一些自己以前没掌握的点儿记录下来. 1.字符串相关 字符串不是基本类型,字符串是一个类,也就是一个引用类型. 字符串转int类型String a ...

  4. 《剑指offer》面试题25. 合并两个排序的链表

    问题描述 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的. 示例1: 输入:1->2->4, 1->3->4 输出:1->1->2-> ...

  5. opencv 4.0 + linux + cuda静态编译

    #下载最新的opencv git clone "https://github.com/opencv/opencv.git" git clone "https://gith ...

  6. Cesium中级教程7 - Geometry and Appearances 几何图形和外观

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 本教程将向您介绍提供使用Primitive API的几何图形和外 ...

  7. 2022GDUT寒训专题一J题

    题目 题面 给你一个长度为 n的整数序列{A1,A2,⋯,A**n},要求从中找出一段连续的长度不超过 m的非空子序列,使得这个序列的和最大. 输入格式 第一行为两个整数 n,m: 第二行为 n个用空 ...

  8. MySQL查询处理——逻辑查询处理和物理查询处理

    对于查询处理,可将其分为逻辑查询处理和物理查询处理.逻辑查询处理表示执行查询应该产生什么样的结果,而物理查询代表MySQL数据库是如何得到结果的. 逻辑查询处理 MySQL真正的执行顺序如下: (8) ...

  9. 返回值String表示视图

    第一种:处理器方法返回String--表示逻辑视图名称(需配置视图解析器) 视图解析器: MyController类中: index.jsp中: 第二种:处理器方法方慧String,表示完整视图路径, ...

  10. Tomcat-IDEA整合Tomcat服务器

    Tomcat(IDEA整合Tomcat服务器) 可以加多个版本tomcat