C++之深浅拷贝

我们都知道,在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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值