c++ 拷贝构造函数、拷贝运算符、析构函数
拷贝构造函数、拷贝运算符、析构函数
拷贝构造函数、拷贝运算符、析构函数
定义行为像值的类
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++ 拷贝构造函数、拷贝运算符、析构函数的更多相关文章
- [c++基础]3/5原则--拷贝构造函数+拷贝赋值操作符
/* * main.cpp * * Created on: Apr 7, 2016 * Author: lizhen */ #include <iostream> #include &qu ...
- 拷贝构造函数 & 拷贝赋值运算符
一.拷贝构造函数 1. 形式 class A { public: // ... A(const A &); // 拷贝构造函数 }; 2. 合成拷贝构造函数 编译器总会为我们合成一个拷贝构造函 ...
- C++ 类 & 对象-类成员函数-类访问修饰符-C++ 友元函数-构造函数 & 析构函数-C++ 拷贝构造函数
C++ 类成员函数 成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义. 需要强调一点,在 :: 运算符之前必须使用类名.调用成员函数是在对象上使用点运算符(.),这样它就能操作与 ...
- 类string的构造函数、拷贝构造函数和析构函数
原文:http://www.cnblogs.com/Laokong-ServiceStation/archive/2011/04/19/2020402.html 类string的构造函数.拷贝构造 ...
- 编写类String的构造函数、拷贝构造函数、析构函数和赋值函数
一.题目: class String { public: String(const char *str = NULL); // 普通构造函数 String(const String &othe ...
- C++基础 (3) 第三天 构造函数 构造函数初始化列表 拷贝构造函数 析构函数 静态成员变量
// 同类之间无私处 2构造函数 3析构函数 4构造函数的种类和析构函数的顺序 结论:析构函数的调用顺序,跟对象的构造顺序相反,谁先构造,谁最后一个被析构. 拷贝构造函数: 注意: 等号写在下面和写在 ...
- C++的转换构造函数、拷贝构造函数、赋值运算符重载
1 转换构造函数 C++的转换构造函数是只有一个参数的构造函数.当程序试图将一个其他类型的对象或基本类型值赋给该类的一个待初始化对象时(如Person p="Dean";) ...
- C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数
构造函数 ,是一种特殊的方法 .主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 .特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数 ...
- 对C++拷贝构造函数的一点理解
一. 什么是拷贝构造函数 先看一个简单的例子: #include <iostream> using namespace std; class CExample { private: int ...
- 如何避免被C++默认拷贝构造函数忽悠?
一.背景介绍 因为工作关系,需要用到C++编程.对于我来说,虽然一直从事的是linux平台下的嵌入式软件开发,但深入用到C++的特性的地方并不多.对于C++,用得最多的无非是指针. ...
随机推荐
- JAVA自定义连接池原理设计(一)
一,概述 本人认为在开发过程中,需要挑战更高的阶段和更优的代码,虽然在真正开发工作中,代码质量和按时交付项目功能相比总是无足轻重.但是个人认为开发是一条任重而道远的路.现在本人在网上找到一个自定义连接 ...
- promise到底怎么理解
Promise的含义promise是异步编程的一种解决方法.所谓promise,简单说是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,从语法上说,promise是一个对象,从 ...
- day 18 C语言顺序结构基础定义1
(1).有以下程序: 程序运行后的输出结果是[B] (A).3,5,5,3 (B).3,5,3,5 (C).5,3,3,5 (D).5,3,5,3 这个题其实也可以弄成改错题,传到函数里面要对其值操作 ...
- JavaScript 中BOM的常用操作
JavaScript BOM操作 1.获取浏览器窗口尺寸 var width=window,innerWidth //获取可视窗口宽度 var height=window.innerHeight // ...
- 触发器中获取sql
CREATE trigger 触发器名 on 表名 for update,delete as set nocount on create table #t(EvebtType varchar(60), ...
- 带你十天轻松搞定 Go 微服务系列(一)
本文开始,我们会出一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建(本文) 服务拆分 用户服务 产品服务 订单服务 支付服务 RPC 服务 Au ...
- java解洛谷P1003铺地毯问题
此题给出的最大地毯数量为10000,创建[10001][4]长度的二维数组 以稀松数组的方法,[第i个地毯]的 [0][1][2][3]分别保存地毯的坐标和大小 再用需要求的坐标比较即可 public ...
- Arduino+ESP32 之 驱动GC9A01圆形LCD(一),基于Arduino_GFX库
最近买了一块圆形屏幕,驱动IC是GC9A01,自己参考淘宝给的stm32的驱动例程, 在ubuntu下使用IDF开发ESP32,也在windows的vscode内安装IDF开发ESP32,虽然都做到了 ...
- Lesson1——Pandas是什么
pandas目录 一.简介 Pandas 是一个开源的第三方 Python 库,从 Numpy 和 Matplotlib 的基础上构建而来,享有数据分析"三剑客之一"的盛名(Num ...
- BootStrap基础入门概述总结
是否还值得学习BootStrap 因为自己还是学生,自己在学习之前就先在网上看了看BootStrap是否在现在依旧流行,是否还值得学习. 以下是网友的一些评价: 20年11月 Bootstrap作为入 ...