[C++]String类的理解

如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”

的方式自动生成缺省的函数(浅拷贝)。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。
以类 String 的两个对象 a,b 为例,假设 a.m_data 的内容为“hello”, b.m_data 的内容为“world”。现将a赋给b,缺省赋值函数的“位拷贝”意味着执行b.m_data= a.m_data。 这将造成三个错误:
1. b.m_data 原有的内存没被释放,造成内存泄露;
2. b.m_data 和 a.m_data 指向同一块内存,a 或 b 任何一方变动都会影响另一方;
3. 在对象被析构时,m_data 被释放了两次。
故需要深拷贝

下面是最容易理解的一种写法:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string.h>
using namespace std;
class String
{
    friend ostream& operator<<(ostream &out,String &rhs);
    friend istream& operator>>(istream &in,String &rhs);

public:
    String(char *str = "")
        :_pstr(new char[strlen(str)+1])
    {
        strcpy(_pstr,str);
    }

    String(const String& s)
        :_pstr(new char [strlen(s._pstr)+1])
    {
        strcpy(_pstr,s._pstr);
    }
    String& operator=(const String& other)
    //1.检查自赋值
    //2.用 delete 释放原有的内存资源
    //3.分配新的内存资源,并复制字符串
    //4.返回本对象的引用(连等)
    {
        if(this != &other)
        {
            delete []_pstr;
            _pstr = new char [strlen(other._pstr)+1];
            strcpy(_pstr,other._pstr);
        }
        return *this;
    }

    ~String()
    {
        if(_pstr != NULL)
        delete []_pstr;
    }
    void Debug()
    {
        cout<<"string:> "<<_pstr<<endl;
        cout<<"count:> "<<*_ref<<endl;
    }
private:
    char *_pstr;
};
ostream& operator<<(ostream &out,String &rhs)
{
    out<<rhs._pstr;
    return out;
}
istream& operator>>(istream &in,String &rhs)
{
    in>>rhs._pstr;
    return in;
}

void Fun1()
{
    String A("aaaaa");
    String B(A);
    String C = B;
    C = A;
    cin>>C;
    cout<<C<<endl;
}

下面还有一种简洁的拷贝构造和赋值运算符重载函数:
理解图:
这里写图片描述

    String(const String& s)
        :_pstr(NULL)//没有指向的指针不能交换里面的内容
    {
        String temp(s._pstr);
        std::swap(temp._pstr,_pstr);
    }

    String& operator=(String rhs)//运算符重载隐含了左值,故不需要赋空
    //此时没有将参数赋为const String& other的原因:
    //将下面实现换成注释也可完成同样功能,但重复了上面的拷贝构造内容,如换成没有注释的形式,则增强代码重用率,在下面进行交换的时候可以重复调用拷贝构造函数,也可达到赋值的目的。
    {
        //String temp(s._pstr);
        //std::swap(temp._pstr,_pstr);
        std::swap(_pstr,rhs._pstr);
        return *this;
    }//返回值联等  参数会产生临时变量

然而深拷贝并不见的完全好,如果多个对象都存储了相同的内容,那岂不是很浪费空间,于是我们又发明了一个叫做引用计数的东西,就是把相同内容的对象只存储一遍,用一个计数器记下他的数目,每构造一次将其置一,每拷贝构造一次将其加一,每赋值一次将其加一(原来的引用计数减一),但如果将引用计数设为 int _ref 我们每次进行操作都会对其原来的引用计数加一(其实他原来的_ref并不需要改变,甚至在调用赋值重载时还会减一),而将其换为 static int _ref 时,如果我们申请了新的对象

比如:
    String A;
    String B(A);

    String C;

C的出现会将前面A B的引用计数一同改变,所以这也不是什么好办发,然而最好的办法是将引用计数设为指针
int * _ref 这样实现起来解容易多了,也可像库函数STL实现一样将该引用计数放于私有成员字符串的前4个字节,节省了一个私有成员的开销,这种方法与库函数中的new类似,new = 开辟空间(malloc)+调用构造函数(定位new) new operator = 开辟空间。
下面是写时拷贝的实现:

class String
{
    friend ostream& operator<<(ostream &out,String &rhs);
    friend istream& operator>>(istream &in,String &rhs);

public:
    String(char *str = "")
        :_pstr(new char[strlen(str)+1])
        ,_ref(new int[1])//申请一个4字节的空间  new int(1)
    {
        strcpy(_pstr,str);
        _ref[0]=1;//里面存放计数
    }

    String(const String &s)
        :_pstr(NULL)
        ,_ref(NULL)
    {
        _pstr = s._pstr;
        _ref = s._ref;
        (*_ref)++;
    }

    String& operator=(const String &rhs)
    {
        if(rhs._pstr != this->_pstr)
        {
            if( --(*_ref) == 0)
            {
                delete []_pstr;
                if(_ref != NULL)//_ref可能为空
                    delete []_ref;
            }
            _pstr = rhs._pstr;
            _ref = rhs._ref;
            (*_ref)++;//_red[0]++
        }
        return *this;
    }
    ~String()
    {
        if(--(*_ref) == 0)
        {
            delete []_pstr;
            if(_ref != NULL)
                delete []_ref;
        }
    }
    void Debug()
    {
        cout<<"string:> "<<_pstr<<endl;
        cout<<"count:> "<<*_ref<<endl;
    }
private:
    char *_pstr;
    int *_ref;
};
ostream& operator<<(ostream &out,String &rhs)
{
    out<<rhs._pstr;
    return out;
}
istream& operator>>(istream &in,String &rhs)
{
    in>>rhs._pstr;
    return in;
}
void Fun2()
{
    String a("qqq");
    a.Debug();
    String b("dfhdfhd");
    //a.Debug();
    String c(b);
    b.Debug();
    c = a;
    a.Debug();
    b.Debug();
    c.Debug();
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值