写时拷贝 引用计数器模型

1、深浅拷贝的使用时机:

浅拷贝:对只读数据共用一份空间,且只释放一次空间;

深拷贝:数据的修改,的不同空间;

2、引用计数器模型

使用变量use_count,来记载初始化对象个数;

(1)、static模型(此处只用浅拷贝与浅赋值)

#include<iostream>
#include<string.h>
#include<malloc.h>
using namespace std;

class String{
public:
    String(const char *str = ""){
        if(str == NULL){
            data = new char;
            data[0] = 0;
        }else{
            data = new char[strlen(str) + 1];
            strcpy(data,str);
        }
        use_count++;     //每新创建一个对象,对引用计数器++;
    }
    String(const String &s){
        data = s.data;
        use_count++;      //创建出新的对象,use_count++;
    }
 //此处先不写赋值语句
    ~String(){
        if(--use_count == 0){  //当引用计数器减为0时,就是每次行析构对象时,都对它减一次,直到为0才释放空间,
            delete []data;
            data = NULL;
        }
    }
public:
    char* GetString()const{
        return data;
    }
private:
    char *data;
    static int use_count;   //此处use_count只有一份,负责记载创建了多少个对象;
};

int String::use_count = 0;  //C++中的静态变量全局只有一份,可以再类外进行初始化;

int main(void)
{
    String s1("hello");
    cout<<s1.GetString()<<endl;
    String s2;
    s2 = s1;  //浅赋值,调用默认的;
    cout<<s2.GetString()<<endl;
    String s3("xyz"); //创建t3对象,要出问题了;(对其就只创建出来,不在进行赋值语句等操作);此时的情况是:已经有两个对象,其成员data指向同一空间,此时又有一个data指向另一个空
                      //间,但是use_count为0才释放空间,只释放一份,所以肯定有内存泄漏!!!
    return 0;
}

上面的static浅拷贝其实存在很大的问题,当t3对象创建时,use_count会加1;

当调用析构函数时,每次减1,为0时,释放空间,

3、写时拷贝

浅拷贝与深拷贝联合使用,看实际需求对其编写,此时我希望,对数据读时共用一份数据,需要修改时,在单独开辟空间进行修改,在进行改写,并且对象(初始化)应该有自己的data和use_count,赋值语句时共用一份就行,此时就需要句柄了,这就是写时拷贝;

具体完整代码如下:

#include<iostream>
#include<malloc.h>
#include<string.h>
using namespace std;
class String;
class String_rep{        // 这个类是封装在内部供我们程序员自己使用的。
    friend class String;  //友元类,可以访问私有数据。
public:
    String_rep(const char *str = ""):use_count(0){  //构造函数的初始化
        if(str == NULL){
            data = new char[1];
            data[0] = 0;
        }else{
            data = new char[strlen(str)+1];
            strcpy(data,str);
        }
    }
    String_rep(const String_rep &rep);
    String_rep& operator=(const String_rep &rep);
    ~String_rep(){
        delete []data;
        data = NULL;
    }
public:
    void increment(){  
        use_count++;
    }
    void decrement(){   //这个函数至关重要,写了一个释放空间的函数,要在其后赋值语句中使用;
        if(--use_count == 0)
            delete this;
    }
    int use_count_()const{
        return use_count;
    }
public:
    char *getdata()const{
        return data;
    }
private:
    char *data;
    int use_count;
};
class String{
public:
    String(const char *str = ""):rep(new String_rep(str)){
        rep->increment();
    }
    String(const String &s){
        rep = s.rep;
        rep->increment();
    }
    String& operator=(const String &s){
        if(this != &s){
            rep->decrement();
            rep = s.rep;
            rep->increment();
        }
        return *this;
    }
    ~String(){
        rep->decrement();
    }
public:
    int use_count()const{
        return rep->use_count_();
    }
    void print()const{
        cout<<rep->data<<endl;
    }
    void toupper(){      //这个函数提供的意义:对其要改的对象重新申请空间,进行改写,使相互之间不影响。     
        if(rep->use_count_() > 1){ //对象个数大于1才进行拷贝出来重新写,只有一个就直接在其上进行修改。
            String_rep *new_rep = new String_rep(rep->data);
            this->rep->decrement();
            rep = new_rep;
            rep->increment();
        }
        char *pch = rep->data;
        while(*pch){
            *pch -= 32;
            pch++;
        }
    }
private:
    String_rep *rep; // 句柄
};
int main(){
    String s1("hello");
    String s2 = s1;
    String s3("xyz");
    s3 = s2;
    s1.toupper();
    s1.print();
    cout<<"s1 count = "<<s1.use_count()<<endl;
    s2.print();
    cout<<"s2 count = "<<s2.use_count()<<endl;
    s3.print();
    cout<<"s3 count = "<<s3.use_count()<<endl;
    return 0;
}

以上的代码就可以达到目的,每次创建对象都有自己的data和use_count(调用构造函数),在赋值语句时先释放原有空间,在进行浅拷贝,构造函数时也是浅拷贝,对其进行了各自空间的管理与释放,并且在修改数据时拷贝出来一份即可。

分析如下:

关于以上还有个问题:就是:

 void decrement(){   
        if(--use_count == 0)
            delete this;
    }

为什么不在String类的析构函数中写delete呢?

原因:析构函数只有在对象释放是才调用,而在此时,通过赋值语句要释放空间,析构函数此时就不能及时释放原有空间,会造成内存泄漏,所以写一个释放空间的函数,内部有delete,会先调用析构函数,达到了及时释放空间的目的!

以上只是对写时拷贝的粗浅理解。

时间: 2024-10-04 17:37:47

写时拷贝 引用计数器模型的相关文章

深拷贝&amp;浅拷贝&amp;引用计数&amp;写时拷贝

(1).浅拷贝: class String { public: String(const char* str="") :_str(new char[strlen(str)+1]) { strcpy(_str,str); } ~String() { if(NULL!=_str) { delete[] _str; _str=NULL; } } private: char* _str; }; int main() { String s1("hello"); String

写时拷贝技术

Copy On Write(COW):写时拷贝技术 一.什么是写时拷贝技术: 写时拷贝技术可以理解为"写的时候才去分配空间",这实际上是一种拖延战术. 举个栗子: 二.写时拷贝技术原理: 写时拷贝技术是通过"引用计数"实现的,在分配空间的时候多分配4个字节,用来记录有多少个指针指向块空间,当有新的指针指向这块空间时,引用计数加一,当要释放这块空间时,引用计数减一(假装释放),直到引用计数减为0时才真的释放掉这块空间.当有的指针要改变这块空间的值时,再为这个指针分配自

String 类的实现(4)写时拷贝浅析

由于释放内存空间,开辟内存空间时花费时间,因此,在我们在不需要写,只是读的时候就可以不用新开辟内存空间,就用浅拷贝的方式创建对象,当我们需要写的时候才去新开辟内存空间.这种方法就是写时拷贝.这也是一种解决由于浅拷贝使多个对象共用一块内存地址,调用析构函数时导致一块内存被多次释放,导致程序奔溃的问题.这种方法同样需要用到引用计数:使用int *保存引用计数:采用所申请的4个字节空间. 1 #include<iostream> 2 #include<stdlib.h> 3 using

string类的写时拷贝

由于浅拷贝使多个对象共用一块内存地址,调用析构函数时导致一块内存被多次释放,导致程序奔溃. 实现string类的时候通常显示的定义拷贝构造函数和运算符重载函数. 由于释放内存空间,开辟内存空间时花费时间,因此,在我们在不需要写,只是读的时候就可以不用新开辟内存空间,就用浅拷贝的方式创建对象,当我们需要写的时候才去新开辟内存空间.这种方法就是写时拷贝. 在构造函数中开辟新的空间时多开辟4个字节的空间,用来存放引用计数器,记录这快空间的引用次数. [cpp] view plain copy #inc

简单的String类实现及写时拷贝

#include<iostream> using namespace std; class String { public: /*String(const char* str=" ") :_str(new char[strlen(str)+1]) { strcpy(_str, str); } */ String(const char* str = " ") { if (str == NULL) { _str = new char; _str[0] = '

String写时拷贝实现

头文件部分 1 /* 2 版权信息:狼 3 文件名称:String.h 4 文件标识: 5 摘 要:对于上版本简易的String进行优化跟进. 6 改进 7 1.(将小块内存问题与大块分别对待)小内存块每个对象都有,当内存需求大于定义大小时利用动态分配 8 2.实现大块内存的写时拷贝功能,提高效率,优化空间利用 9 3.类似new[]实现机制:将动态内存块大小信息保存为隐藏“头” 10 11 当前版本:1.2 12 修 改 者:狼 13 完成日期:2015-12-12 14 15 取代版本:1.

C++ String类写时拷贝

    维基百科:     写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略.其核心思想是,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变.这过程对其他的调用者都是透明的(transparently).此作法主要的优点是如果调用者没有修

c++ 写时拷贝

写时拷贝--Copy On Writ #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; class String { public:  String(char *str = "") :_str(new char[strlen(str)+5])  {   _str += 4;   _GetRefCount(_str) = 1;   strcpy(_str, str);  }  Str

写时拷贝(方案一)

深拷贝效率低,我们可以应引用计数的方式去解决浅拷贝中析构多次的问题. 首先要清楚写时拷贝是利用浅拷贝来解决问题!! 方案一 class String { private:     char* _str;     int _refCount; }; 方案一最不靠谱,它将用作计数的整形变量_refCount定义为类的私有成员变量,任何一个对象都有它自己的成员变量_refCount,它们互不影响,只要拷贝出了对象,_refCount大于了1,那么每个对象调用自己的析构函数时--_refCount不等于