C++ 拷贝控制和资源管理,智能指针的简单实现
C++ 关于拷贝控制和资源管理部分的笔记,并且介绍了部分C++ 智能指针的概念,然后实现了一个基于引用计数的智能指针。关于C++智能指针部分,后面会有专门的研究。
通常,管理类外资源的类必须定义拷贝控制成员。为了定义这些成员,我们首先必须确定此对象的拷贝语义。一般来讲,有两种选择:
使类的行为看起来像一个值
类的行为像一个值:意味着它有自己的状态。当我们拷贝一个像值的对象时,副本和原对象是完全对立的。改变副本不会对对原对象有任何影响。反之亦然。
使类的行为看起来像一个指针
行为像指针的类则共享状态。当我们拷贝一个这种类的对象时,副本和原对象使用相同的底层数据。改变副本也会改变原对象,反之亦然。
行为像值的类
- 为了提供类值的行为,对于类管理的资源,每个对象应该都拥有一份自己的拷贝。
- 类值拷贝赋值运算符
通常组合了析构函数和构造函数的操作。类似析构函数,赋值操作会销毁左侧运算对象的资源。类似构造函数,赋值操作会从右侧运算对象拷贝数据。
编写赋值运算符时,有两点需要注意:
1. 如果将一个对象赋予它自身,赋值运算符必须能正确工作
2. 大多数赋值运算符组合了析构函数和拷贝构造函数的工作
当编写一个赋值运算符时,一个好的方法是先将右侧运算对象拷贝到一个局部临时对象中,然后再销毁左侧运算对象就是安全的了。
定义行为像指针的类
引用计数
引用计数的工作方式如下:
1. 除了初始化对象之外,每个构造函数(拷贝构造函数除外)还要创建一个引用计数,用来记录有多少对象正在与创建的对象共享状态。当我们创建一个对象时,只有一个对象共享状态,因此计数器初始化为1。
2. 拷贝构造函数不分配新的计数器,而是拷贝给定对象的数据成员,包括计数器。拷贝构造函数递增共享的计数器,指出给定对象的状态又被一个新用户共享。
3. 析构函数递减计数器,指出共享状态的用户少了一个。如果计数器变为0,则析构函数释放状态。
4. 拷贝赋值运算符递增右侧运算对象的计数器,递减左侧运算对象的计数器。如果左侧运算对象的计数器变为0,意味着它的共享状态没有用户了,拷贝赋值运算符就必须销毁状态。
引用计数的存放位置:一种方法只保存在动态内存中。当创建一个对象时,我们也分配一个计数器。当拷贝或赋值对象时,我们拷贝指向计时器的指针。使用这种方法,副本和原对象都会指向相同的计数器。
下面给出一个基于引用计数的共享智能指针的实现。
#include<iostream>
using namespace std; template<class T>
class SmartPtr{
public:
//构造函数
SmartPtr(T *sp = NULL)
:m_ptr(sp), use_count(new std::size_t(1)){} //拷贝构造函数
SmartPtr(const SmartPtr<T> &ref){
if (this != &ref){
m_ptr = ref.m_ptr;
use_count = ref.use_count;
++*use_count;
}
} //拷贝赋值运算符
SmartPtr& operator=(const SmartPtr<T> &ref){
if (this == &ref) //处理自赋值情况
return *this; //判断原use_count是否为0
--*use_count;
if (*use_count == 0){
delete m_ptr;
delete use_count;
m_ptr = NULL;
use_count = NULL;
cout << "operator= delete" << endl;
} m_ptr = ref.m_ptr;
use_count = ref.use_count;
++*use_count;
} //析构函数
~SmartPtr(){
--*use_count;
if (*use_count == 0){
delete m_ptr;
delete use_count;
m_ptr = NULL;
use_count = NULL;
cout << "~SmartPtr() and delete" << endl;
}
else
cout << "~SmartPtr() use_count:" << *use_count <<endl;
} T * get(){
return m_ptr;
} std::size_t getUse_count(){
return *use_count;
}
private:
//基础对象指针
T *m_ptr;
std::size_t *use_count;
}; class Test{
public:
Test(int a, char b) :_a(a), _b(b){}
void print(){
cout << _a << " " << _b << " ";
}
private:
int _a;
char _b;
}; int main(){
//内置数据类型测试
cout << "内置数据类型测试" << endl;
SmartPtr<int> sp1(new int(5)); //默认构造函数
cout << *sp1.get() << " " << sp1.getUse_count() << endl; SmartPtr<int> sp2(sp1); //拷贝构造函数
cout << *sp2.get() << " " << sp2.getUse_count() << endl; SmartPtr<int> sp3;
sp3 = sp1; //拷贝赋值运算符
cout << *sp3.get() << " " << sp3.getUse_count() << endl; sp3 = sp3; //自赋值情况
cout << *sp3.get() << " " << sp3.getUse_count() << endl; //自定义数据类型测试
cout << endl << endl << "自定义数据类型测试" << endl;
SmartPtr<Test> tp1(new Test(10, 'c')); //默认构造函数
(tp1.get())->print();
cout << tp1.getUse_count() << endl; SmartPtr<Test> tp2(tp1); //拷贝构造函数
(tp2.get())->print();
cout << tp2.getUse_count() << endl; SmartPtr<Test> tp3;
tp3 = tp1; //拷贝赋值运算符
(tp3.get())->print();
cout << tp3.getUse_count() << endl; tp3 = tp3; //自赋值情况
(tp3.get())->print();
cout << tp3.getUse_count() << endl; cout << endl << endl << "析构函数" << endl;
return 0;
}
程序运行结果如下:
C++ 拷贝控制和资源管理,智能指针的简单实现的更多相关文章
- 【C++ Primer 第13章】2. 拷贝控制和资源管理
拷贝控制和资源管理 • 类的行为像一个值.意味着它应该有自己的状态,当我们拷贝一个像值得对象时,副本和原对象是完全独立的,改变副本不会对原对象有任何影响. • 行为像指针的类则共享状态.当我们拷贝一个 ...
- [C++]类的设计(2)——拷贝控制(拷贝控制和资源管理)
1.类的行为分类:看起来像一个值:看起来想一个指针. 1)类的行为像一个值,意味着他应该有自己的状态.当我们拷贝一个像值的对象时,副本和原对象是完全独立的.改变副本不会对原有对象有任何影响 ...
- 必须要注意的 C++ 动态内存资源管理(二)——指针对象简单实现
必须要注意的 C++动态内存资源管理(二)——指针对象简单实现 四.拷贝类型的资源 上节我们说过,对于图片类型的资源我们有时候往往采用拷贝(如果对于那种公共图片,可能采用唯一副本,提供 ...
- 【C++】智能指针auto_ptr简单的实现
//[C++]智能指针auto_ptr简单的实现 #include <iostream> using namespace std; template <class _Ty> c ...
- C++智能指针及其简单实现
本文将简要介绍智能指针shared_ptr和unique_ptr,并简单实现基于引用计数的智能指针. 使用智能指针的缘由 1. 考虑下边的简单代码: int main() { ); ; } 就如上边程 ...
- C++ Primer : 第十三章 : 拷贝控制之拷贝控制和资源管理
定义行为像值的类 行为像值的类,例如标准库容器和std::string这样的类一样,类似这样的类我们可以简单的实现一个这样的类HasPtr. 在实现之前,我们需要: 定义一个拷贝构造函数,完成stri ...
- C++:(拷贝,继承,智能指针)练习
#include <iostream> #include <string> #include <memory> #include <functional> ...
- C++ 引用计数技术及智能指针的简单实现
一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...
- C++ 智能指针的简单实现
智能指针的用处:在c++中,使用普通指针容易造成堆内存的泄露问题,即程序员会忘记释放,以及二次释放,程序发生异常时内存泄漏等问题,而使用智能指针可以更好的管理堆内存.注意,在这里智能指针是一个类而非真 ...
随机推荐
- css控制图片与文字对齐
文字旁边搭配图片时,发现图片比文字靠上,原来默认的情况是图片顶对齐而文字底对齐,通过设置css属性可以使得图片与文字对齐. 设置各对象的vertical-align属性,属性说明:baseline-将 ...
- php上线教程----阿里云下设值二级域名并将项目上线
在工作中,我们需要在一个主机地址下分配多个域名来上线多个项目,但是怎么设置一个二级域名并且完成上线项目的,接下来我们就以阿里云为例演示整个上线流程 首先登陆你的阿里云,找到你的域名 点击解析,进入解析 ...
- [JAVA] - Java OutOfMemoryError分类
Java OutOfMemoryError一般常遇到的分为两类,分别提示: "Java heap space" 和 "PermGen space",前面的是指j ...
- JS 基础学习随想
2012年就已经接触过了js,给我的印象:这是一门谈不上复杂的语言.大概这就是所谓的学的越浅,用的越少,觉得自己会的东西好像得更多吧!开始做基础练习题的时候觉得好像都十分简单.可是后来在做到对象数组的 ...
- web及H5 的链接测试
1:先下载一个Xenu工具 2:安装完成之后,进入页面(将弹出框关闭) 3:进行设置(一般不用修改设置) 4:修改完成之后点击工具栏中的file按钮,并输入想要测试的URL地址 5:点击OK测试完成之 ...
- 粗谈shell脚本风格
注意:此风格并非官方版本,为个人在编写和维护脚本程序时总结出来的民间版本.0. 开头:除去开头的#!/bin/bash,最前面的就是脚本描述注释了,视个人喜好而定,例如: ############## ...
- nodejs中的路由
一.路由初步 url.parse(string).query | url.parse(string).pathname | | | | | ------ -------------------http ...
- JAVA开发环境搭建 - Eclipse基本配置
Eclipse设置的内容包括许多方面,不同的开发人员,不同的项目需要,可能对Eclipse的设置不尽相同.如下内容仅是对本人的一些基本设置做一些记录,以作备忘.后期会逐渐对相关内容进行更新,仅供参考. ...
- 【Zookeeper】源码分析之服务器(四)
一.前言 前面分析了LeaderZooKeeperServer,接着分析FollowerZooKeeperServer. 二.FollowerZooKeeperServer源码分析 2.1 类的继承关 ...
- 读书笔记 effective c++ Item 30 理解内联的里里外外 (大师入场啦)
最近北京房价蹭蹭猛涨,买了房子的人心花怒放,没买的人心惊肉跳,咬牙切齿,楼主作为北漂无房一族,着实又亚历山大了一把,这些天晚上睡觉总是很难入睡,即使入睡,也是浮梦连篇,即使亚历山大,对C++的热情和追 ...