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++ 拷贝控制和资源管理,智能指针的简单实现的更多相关文章

  1. 【C++ Primer 第13章】2. 拷贝控制和资源管理

    拷贝控制和资源管理 • 类的行为像一个值.意味着它应该有自己的状态,当我们拷贝一个像值得对象时,副本和原对象是完全独立的,改变副本不会对原对象有任何影响. • 行为像指针的类则共享状态.当我们拷贝一个 ...

  2. [C++]类的设计(2)——拷贝控制(拷贝控制和资源管理)

      1.类的行为分类:看起来像一个值:看起来想一个指针.     1)类的行为像一个值,意味着他应该有自己的状态.当我们拷贝一个像值的对象时,副本和原对象是完全独立的.改变副本不会对原有对象有任何影响 ...

  3. 必须要注意的 C++ 动态内存资源管理(二)——指针对象简单实现

    必须要注意的 C++动态内存资源管理(二)——指针对象简单实现 四.拷贝类型的资源         上节我们说过,对于图片类型的资源我们有时候往往采用拷贝(如果对于那种公共图片,可能采用唯一副本,提供 ...

  4. 【C++】智能指针auto_ptr简单的实现

    //[C++]智能指针auto_ptr简单的实现 #include <iostream> using namespace std; template <class _Ty> c ...

  5. C++智能指针及其简单实现

    本文将简要介绍智能指针shared_ptr和unique_ptr,并简单实现基于引用计数的智能指针. 使用智能指针的缘由 1. 考虑下边的简单代码: int main() { ); ; } 就如上边程 ...

  6. C++ Primer : 第十三章 : 拷贝控制之拷贝控制和资源管理

    定义行为像值的类 行为像值的类,例如标准库容器和std::string这样的类一样,类似这样的类我们可以简单的实现一个这样的类HasPtr. 在实现之前,我们需要: 定义一个拷贝构造函数,完成stri ...

  7. C++:(拷贝,继承,智能指针)练习

    #include <iostream> #include <string> #include <memory> #include <functional> ...

  8. C++ 引用计数技术及智能指针的简单实现

    一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...

  9. C++ 智能指针的简单实现

    智能指针的用处:在c++中,使用普通指针容易造成堆内存的泄露问题,即程序员会忘记释放,以及二次释放,程序发生异常时内存泄漏等问题,而使用智能指针可以更好的管理堆内存.注意,在这里智能指针是一个类而非真 ...

随机推荐

  1. Ffmpeg 视频教程

    最近一段时间找时间录制了一些Ffmpeg视频教程,还有录制完毕,会持续更新,内容会包含Ffmeg保存文件,网络流转发, 编码,解码,播放器制作,以及服务端搭建等等,适合初学者,有需要的朋友的可以关注: ...

  2. Javascript 闭包与高阶函数 ( 一 )

    上个月,淡丶无欲 让我写一期关于 闭包 的随笔,其实惭愧,我对闭包也是略知一二 ,不能给出一个很好的解释,担心自己讲不出个所以然来. 所以带着学习的目的来写一写,如有错误,忘不吝赐教 . 为什么要有闭 ...

  3. mac os x在PC上安装

    系统安装之前的准备工作及安装过程简介 前面我们已经提到,苹果电脑虽然已经采用了x86架构的Intel处理器,但其官方并不提供在非苹果电脑上安装Mac OS的支持.所以,要想在普通PC/笔记本电脑上安装 ...

  4. ASP.NET Core MVC之Serilog日志处理,你了解多少?

    前言 本节我们来看看ASP.NET Core MVC中比较常用的功能,对于导入和导出目前仍在探索中,项目需要自定义列合并,所以事先探索了如何在ASP.NET Core MVC进行导入.导出,更高级的内 ...

  5. 基于Hadoop的改进Apriori算法

    一.Apriori算法性质 性质一: 候选的k元组集合Ck中,任意k-1个项组成的集合都来自于Lk. 性质二: 若k维数据项目集X={i1,i2,-,ik}中至少存在一个j∈X,使得|L(k-1)(j ...

  6. mysql 添加登录用户

    一, 创建用户: 命令:CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 说明:username - 你将创建的用户名, host - 指 ...

  7. 使用grunt完成requirejs的合并压缩和js文件的版本控制

    最近有一个项目使用了 requirejs 来解决前端的模块化,但是随着页面和模块的越来越多,我发现我快要hold不住这些可爱的js文件了,具体表现在每个页面都要设置一堆 requirejs 的配置( ...

  8. FFmpeg入门,简单播放器

    一个偶然的机缘,好像要做直播相关的项目 为了筹备,前期做一些只是储备,于是开始学习ffmpeg 这是学习的第一课 做一个简单的播放器,播放视频画面帧 思路是,将视频文件解码,得到帧,然后使用定时器,1 ...

  9. JavaScript中的函数使用

    append() 是代表改变格子的内容 prev()是代表前一个格子 next()是代表下一个相邻的格子 hide()是代表隐藏 show()是代表显示 childen()是代表子节点 eq()是代表 ...

  10. 「征文」在 cordova 中使用极光统计服务

    写在前面:年前的时候,极光社区组织了一场征文活动 ,收到不少好的文章.现在打算和大家一起分享一下这些优秀的作品 :) 作者:Wilhan - 极光 原文:在 cordova 中使用极光统计服务 正文 ...