C++进阶:写时拷贝技术

拷贝控制

C++提供两个拷贝控制函数

  • 拷贝构造函数
  • 拷贝赋值运算符重载

例如:String

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;
};

C++11增加了移动构造函数和移动赋值运算符重载。

深拷贝与浅拷贝

概念

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

  • 深拷贝:重现分配堆内存,拷贝指针指向内容。
    例如:String类

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(str.data) << ':' << str.data;
}

测试代码

int main(){
        String s1("Hello World");
        String s2 = s1;
        String s3;
        s3 = s1;
        cout << s1 << endl;
        cout << s2 << endl;
        cout << s3 << endl;
}

比较

优点 缺点
浅拷贝 只有一份数据,节省空间。 因为多个指针指向同一个空间,容易引发同一内存多次释放的问题。
深拷贝 每个指针指向不同地址,没有同一内存多次释放的问题。 存在多份相同数据,浪费空间。

浅拷贝与深拷贝的优缺点分别互为彼此的优缺点。有什么办法可以兼有二者的优点?
主要解决问题:

  1. 数据相同时只有一份内存。
  2. 不会出现多次释放问题。

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

计数器技术就是兼有浅拷贝与深拷贝优点的一种技术。


在类声明中添加了计数器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(str.base) << ':' << str.base->data;
}

写时拷贝技术

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

String& String::operator+=(const String& str){
        base.count--;
        // 一些具体操作...
        base = new StringBase();
        base.count++; 
        return *this;
}

优化

上面写时拷贝存在两次层动态处理,其实可以合为一层处理。

你可能感兴趣的:(C++进阶:写时拷贝技术)