C++之深浅拷贝

来源:互联网 发布:mysql create trigger 编辑:程序博客网 时间:2024/05/20 06:24

我们都知道,在C++中会用一个类变量构造同类变量时会用到拷贝构造函数。对于一般变量,我们在使用过程中并没有什么问题,定义也非常简单,一般我们所使用的都是浅拷贝,其实浅拷贝和深拷贝各有各的好处:
浅拷贝节省空间,但有时会出错,深拷贝更加安全,但有时候会造成不必要的空间浪费。

观察下面函数:

//如果Test类只有一个data值时:Test(const Test &t){    data = t.data;}

显而易见,这个函数并没有什么错误,也能完成它的功能。但是,我们看下下面的例子,如果用浅拷贝整个程序运行通过不了了:

#include <iostream>#include <malloc.h>#include <string.h>using namespace std;class String{public:    String(const char *str = NULL)    {        cout << "String()!!" << endl;        if(str == NULL){            data = (char *)malloc(sizeof(char));            data[0] = '\0';        }else{            data = (char *)malloc(strlen(str) + 1);            strcpy(data, str);        }    }    String(const String &str)    {        cout << "String(const String &str)!!" << endl;        data = str.data;    }    char *GetData()    {        return this->data;    }    ~String()    {        cout << "~String()!!" << endl;        free(data);    }private:    char *data;};int main(){    String str("Hello");     //构造函数    String str1;    str1 = str;              //赋值运算    String str2(str);        //拷贝构造    cout << "str = " << str.GetData() << endl;    cout << "str1 = " << str1.GetData() << endl;    cout << "Str2 = " << str2.GetData() << endl;    return 0;}

运行发现程序core dumped。
这里写图片描述
很容易发现,程序调用了两次构造函数,一次拷贝构造函数,但析构了两个然后就core dumped了。这就是因为浅拷贝的问题:

在拷贝构造时,我们只是浅拷贝让str1的data指向了str的data空间,虽然是两个不同String变量,但内部data实际上指向的时同一空间,但是程序结束时,str和str1分别调用析构函数来释放它们内部的data值,这样该空间被释放了两次,所以程序运行到最后崩溃。

这里,我们解决的方法是,在String类中增加一个use_count计数值,让它记录指向data的变量个数。如果浅拷贝一次,use_count++,如果要改变某一个变量中的data值,先判断它的use_count值,如果只有它一个指向data时,直接更改,否则,要先对他进行深拷贝同时给use_count-1,然后在对它拷贝来的data值进行修改。析构同理,当use_count减为1时在析构,下面是代码实现:

#include <iostream>#include <stdio.h>#include <string.h>#include <malloc.h>using namespace std;class String;ostream& operator<<(ostream &out, const String &s);class String_rep{      friend class String;    friend ostream& operator<<(ostream &out, const String &s);public:    String_rep(const char *str = ""):use_count_(0)    {        data = new char[strlen(str) + 1];        strcpy(data, str);    }    String_rep(const String_rep &s);    String_rep& operator=(const String_rep &s)    {        //if(this != &s){         //   data = s.rep->data;            //increment();        //}        return *this;    }    ~String_rep()    {        delete data;    }public:    void increment(){++use_count_;}    void decrement()    {        //--use_count_;        if(--use_count_ == 0){            delete this;        }    }    int use_count() const    {        return use_count_;    }private:    char *data;    int use_count_;} ;class String{    friend ostream& operator<<(ostream &out, const String &s);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()    {        //int i = this->use_count;        //if((--(rep->use_count_)) == 1){            //free(rep->data);            //delete rep;        //}        rep->decrement();    }public:    int use_count() const    {        return rep->use_count();    }    void to_upper()    {        char *ch = rep->data;        if(this->use_count() == 1){            while(*ch != '\0'){                *ch -= 32;                ch ++;            }        }else{            (rep->use_count_) --;            rep = new String_rep(rep->data);            strcpy(rep->data, ch);            (rep->use_count_)++;            ch = rep->data;            while(*ch != '\0'){                *ch -= 32;                ch ++;            }        }    }private:    String_rep *rep;}s;ostream& operator<<(ostream &out, const String &s){    out << (s.rep)->data;    return out;}int main(){    String s1("abcd");    cout << "s1 = " << s1;    cout << " s1 use_count = " << s1.use_count() << endl;    String s2 = s1;    cout << endl << "s2 = " << s2;    cout << " s2 use_count = " << s2.use_count() << endl;    String s3;    s3 = s1;    cout << endl << "s3 = " << s3;    cout << " s3 use_count = " << s3.use_count() << endl;    s2.to_upper();    cout << endl << "s1 = " << s1;    cout << " s1 use_count = " << s1.use_count() << endl;    cout << "s2 = " << s2;    cout << " s2 use_count = " << s2.use_count() << endl;    cout << "s3 = " << s3;    cout << " s3 use_count = " << s3.use_count() << endl;    }

运行结果;
这里写图片描述

在拷贝构造时,深拷贝、浅拷贝各有千秋,我们只有恰当的使用它们,才能程序消耗空间尽可能小而且稳定。

0 1
原创粉丝点击