String类——深拷贝(高级版 & 普通版)

10 篇文章 0 订阅

String类


String类的构造函数、拷贝构造函数、赋值函数和析构函数的在面试和笔试中是经常见到的,如果能准确写出来,那就具备c++基本功的一大半了。
在这个类中包括了指针类成员变量_data,当类中包括指针类成员变量时,一定要重载其拷贝构造函数、赋值函数和析构函数,这既是对C++程序员的基本要求,也是《Effective C++》中特别强调的条款。

深拷贝和浅拷贝的区别
(1)简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误!
(2)浅拷贝 只拷贝指针,深拷贝就是拷贝他的值,重新生成的对像。就像是浅拷贝就是你的影子,深拷贝是你的克隆人,你没了影子也就没了,但是克隆人还活着。

以下为深拷贝的正确写法

#include<iostream>
using namespace std;
//String类——管理字符串——深拷贝—简洁版(高级版)
class String
{
public:
    String(const char* pStr = "")//构造函数
    {
        if (NULL == pStr)
        {
            _pStr = "";
        }
        _pStr = new char[strlen(pStr) + 1];
        strcpy(_pStr, pStr);
        cout << "String()构造 " << endl;
    }

    String(const String& s) //拷贝构造函数
        :_pStr(NULL)
    {
        String strTmp(s._pStr);
        swap(_pStr, strTmp._pStr);
        cout << "String()拷贝构造 " << endl;
    }

    1、第一种形式——更好
    //String& operator=(const String& s)//赋值操作符重载
    //{
    //  if (this != &s)
    //  {
    //      cout << "String() = 赋值 " << endl;
    //      /*String strTmp(s);*/ //直接调拷贝构造
    //      String strTmp(s._pStr); //直接调构造函数
    //      swap(_pStr, strTmp._pStr);
    //  }
    //  return *this;
    //}

    2、第二种形式
    //String& operator=(const String& s)//赋值操作符重载 ——s就是外部实参的别名
    //{
    //  String strTmp(s);
    //  swap(_pStr, strTmp._pStr);
    //  cout << "String() = 赋值 " << endl;
    //  return *this;
    //}

    //3、第三种方式
    String& operator=(const String& s)//赋值操作符重载
    {
        if (this != &s)
        {
            cout << "String() = 赋值 " << endl;
            char* pStr = new char[strlen(s. _pStr) + 1];
            strcpy(pStr, s._pStr);
            delete _pStr; //释放原空间
            _pStr = pStr; //释放新空间
        }
        return *this;
    }

    4、第四种方式
    //String& operator=(String s)
    //{
    //  swap(_pStr, s._pStr);
    //  cout << "String() = 赋值 " << endl;
    //  return *this;
    //}

    ~String() //析构函数
    {
        if (_pStr)
        {
            cout << "~String() " << endl;
            delete[] _pStr;
        }
    }

private:
    char* _pStr;
};

void test()
{
    String s1("hello");
    String s2(s1);//测试拷贝构造函数

    String s3;
    s3 = s2; //测试赋值运算符重载
}
int main()
{
    test();
    return 0;
}

更确切的说,上面的代码的打印只是为了更好的进行测试
测试结果 :

对结果进行分析


    String s1("hello");  //——————————————构造函数s1
    String s2(s1);//测试拷贝构造函数 ————构造一个临时对象,然后拷贝构造s2
    String s3;  // ——————————————构造函数s3
    s3 = s2; //测试赋值运算符重载————————————s1给s3赋值

最后出了函数作用域就调用析构函数进行销毁,先构造的对象后销毁

在以后的面试和笔试中用这种深拷贝是完全OK的

那么在看了高级版的之后,再看一下普通版的吧,支语区别就自己看吧

//String类——管理字符串——深拷贝—普通版
class String
{
public:
    String(const char* pStr = "")//构造函数
    {
        if (NULL == pStr)
        {
            _pStr = new char[1];
            *_pStr = '\0';
        }
        else
        {
            _pStr = new char[strlen(pStr) + 1];
            strcpy(_pStr, pStr);
        }
        cout << "String()构造 " << endl;
    }

    String(const String& s) //拷贝构造函数
        :_pStr(new char[strlen(s._pStr) + 1])
    {
        cout << "String()拷贝构造 " << endl;
        strcpy(_pStr, s._pStr);
    }

    String& operator=(const String& s)//赋值操作符重载
    {
        if (this != &s)
        {
            cout << "String& =()赋值 " << endl;
            //———1、这种更好  如果开辟失败,对原来的对象不会造成影响
            char* pTmp = new char[strlen(s._pStr) + 1];//申请新空间
            strcpy(pTmp, s._pStr);//拷贝元素
            delete[] _pStr;//释放旧空间
            _pStr = pTmp;//使用新空间

            //————2、一开始释放当前空间,如果开辟失败,以前的空间也不存在了
            /*delete[] _pStr;
            _pStr = new char[strlen(s._pStr) + 1];
            strcpy(_pStr, s._pStr);*/
        }
        return *this;
    }

    ~String() //析构函数
    {
        if (_pStr)
        {
            cout << "~String()析构 " << endl;
            delete[] _pStr;
            _pStr = NULL;
        }
    }

private:
    char* _pStr;
};

void Test()
{
    String s1("hello");
    String s2(s1);
    String s3;
    s3 = s1;
}
int main()
{
    Test();
    return 0;
}

调试结果如下

对以上结果图进行分析

    String s1("hello"); //——————构造函数s1
    String s2(s1);//————————直接拷贝构造s2(在前面拷贝构造函数中没有利用临时对象)
    String s3;//————————构造函数s3
    s3 = s1;//————————将s1赋值给s3

    出了函数之后调用析构函数,一共创建了3个对象,进行3次析构

至于浅拷贝在这里就不说了,在面试和笔试中只要掌握正确的就可以啦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值