六、C++语言进阶:写时拷贝技术

6 写时拷贝技术

  • 定义
    在数据第一次写入到某个存储位置时,首先将原有内容拷贝出来,写到另一位置处,然后再将数据写入到存储设备中,该技术只拷贝在拷贝初始化开始之后修改过的数据。

6.1 拷贝控制

  • C++提供两个拷贝控制函数
    拷贝构造函数
    拷贝赋值运算符重载
  • 实例
class String{
public:
        String(const char* str = NULL);
        String(const String& str);
        String& operator=(const String& str);
        ~String();
        size_t size()const;
        friend ostream& operator<<(ostream& os,String const& str);
private:
        char* data;
};

6.2 深拷贝与浅拷贝

  • 概念

浅拷贝:只拷贝指针地址。
通常默认拷贝构造函数与赋值运算符重载都是浅拷贝。

深拷贝:重现分配堆内存,拷贝指针指向内容。

例如:

String::String(const char* str){
        if(NULL == str){
                data = new char[1];
                data[0] = '\0';
        }else{
                data = new char[strlen(str)+1];
                strcpy(data,str);
        }
}
String::~String(){
        delete [] data;
        data = NULL;
}
String::String(const String& str){
        data = new char[str.size()+1];
        strcpy(data,str.data);
}
String& String::operator=(const String& str){
        if(this != &str){
                delete [] data;
                data = new char[str.size()+1];
                strcpy(data,str.data);
        }
        return *this;
}
inline size_t String::size()const{
        return strlen(data);
}

ostream& operator<<(ostream& os,const String& str){
        return os << static_cast<const void *>(str.data) << ':' << str.data;
}
  • 比较
    在这里插入图片描述
优点缺点
浅拷贝只有一份数据,节省空间。 因为多个指针指向同一个空间,容易引发同一内存多次释放的问题。
深拷贝每个指针指向不同地址,没有同一内存多次释放的问题。 存在多份相同数据,浪费空间。

浅拷贝与深拷贝的优缺点分别互为彼此的优缺点。有什么办法可以兼有二者的优点?
主要解决问题:
数据相同时只有一份内存。
不会出现多次释放问题。

6.3 计数器技术:数据相同共享一份内存

计数器技术就是兼有浅拷贝与深拷贝优点的一种技术。
在这里插入图片描述
在类声明中添加了计数器s_count

class String{
public:
        String(const char* str = NULL);
        String(const String& str);
        String& operator=(const String& str);
        ~String();
        size_t size()const;
        friend ostream& operator<<(ostream& os,String const& str);
private:
        char* data;
        static int s_count;
};

实现中增加计数处理

int String::s_count = 0;
String::String(const char* str){
        if(NULL == str){
                data = new char[1];
                data[0] = '\0';
        }else{
                data = new char[strlen(str)+1];
                strcpy(data,str);
        }
        ++s_count;
}
String::~String(){
        if(--s_count == 0){
                delete [] data;
                data = NULL;
        }
}
String::String(const String& str):data(str.data){
        ++s_count;
}
String& String::operator=(const String& str){
        if(this != &str){
                data = str.data;
                ++s_count;
        }
        return *this;
}

问题:构造新对象的计数是几?计数器技术会导致构造新对象计数错误。
解决:每个空间应该具有自己的引用计数,而不能所有空间共享一个引用计数。
在这里插入图片描述

  • 头文件
class String{
        struct StringBase{
                StringBase(const char* str);
                ~StringBase();
                char* data;
                int count;
        };
public:
        String(const char* str = NULL);
        String(const String& str);
        String& operator=(const String& str);
        ~String();
        size_t size()const;
        friend ostream& operator<<(ostream& os,String const& str);
private:
        StringBase* base;
};

实现

String::StringBase::StringBase(const char* str){
        if(NULL == str){
                data = new char[1];
                data[0] = '\0';
        }else{
                data = new char[strlen(str)+1];
                strcpy(data,str);
        }
        ++count;
}
String::StringBase::~StringBase(){
        if(--count == 0){
                delete [] data;
                data = NULL;
        }
}

String::String(const char* str){
        base = new StringBase(str);
}
String::~String(){

}
String::String(const String& str):base(str.base){
        base->count++;
}
String& String::operator=(const String& str){
        if(this != &str){
                base->count--;
                base = str.base;
                base->count++;
        }
        return *this;
}
inline size_t String::size()const{
        return strlen(base->data);
}

ostream& operator<<(ostream& os,const String& str){
        return os << static_cast<const void *>(str.base) << ':' << str.base->data;
}

6.4 写时拷贝技术

以上都是拷贝复制操作,如果字符串发生改变,那么才是真正的写时拷贝。
例如:实现+=操作

String& String::operator+=(const String& str){
        base.count--;
        // 一些具体操作...
        base = new StringBase();
        base.count++; 
        return *this;
}
  • 实例
#include <iostream>
#include <cstring>
#include <memory>

using namespace std;

class String {
    shared_ptr<char[]> ptr;
    // char* s; // ptr.get()
public:
    String(const char* s) {
        char* t = new char[strlen(s)+1];
        strcpy(t,s);
        ptr = shared_ptr<char[]>(t); //t智能指针
    }
    ~String() {
        cout << __func__ << endl;
    }
    size_t size()const {
        return strlen(ptr.get());
    }
    /*
        String(const String& s){}
        String& operator=(const String& s){}
        */
    friend ostream& operator<<(ostream& os,const String& s) {
        return os << (void*)s.ptr.get() << ":" << s.ptr.get();
    }
    char& operator[](int i);
    String operator+(const String& s)const {
        char* t = new char[size()+s.size()+1];
        strcpy(t,ptr.get());
        strcat(t,s.ptr.get());
        return String(t);
    }
};
int main() {
    String s("Hello,World");
    cout << s << endl;

    String t = s;
    t = "def"; //String("def")为临时对象,用完即析构
    cout << s << endl;
    cout << t << endl;

    cout << s+t << endl;
}

结果:

[root@localhost 5]# ./a.out 
0x1d59e70:Hello,World
~String
0x1d59e70:Hello,World
0x1d5a2c0:def
0x1d5a320:Hello,Worlddef
~String
~String
~String
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值