前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++ string实现

C++ string实现

原创
作者头像
evenleo
修改2020-04-13 11:24:09
1.3K0
修改2020-04-13 11:24:09
举报
文章被收录于专栏:C++的沉思C++的沉思C++的沉思

string经典实现

作为C++从业者,我相信都会被考察过实现简单的string类,包括构造、析构、拷贝构造以及赋值拷贝等,因为这能够很好的考察面试者的C++基本功。借看《剑指offer》的机会将这个问题重新整理一下。具体实现如下:

class string {
public:
    string(const char* cstr = nullptr) 
    {
        if (cstr) {
            m_data = new char[strlen(cstr) + 1];
            strcpy(m_data, cstr);
        } else {
            m_data = new char[1];
            *m_data = '\0';
        }
    }
    string(const string& str) 
    {
        m_data = new char[strlen(str.m_data) + 1];
        strcpy(m_data, str.m_data);
    }
    ~string()
    {
        delete[] m_data;
    }
    string& operator = (const string& rhs) 
    {
        if (this == &rhs)
            return *this;

        delete[] m_data;
        m_data = new char[strlen(rhs.m_data) + 1];
        strcpy(m_data, rhs.m_data);
        return *this;
    }

    friend ostream& operator << (ostream& os, const string& rhs)
    {
        return os << rhs.m_data;
    }
private:
    char* m_data;
};

赋值拷贝

上述代码赋值拷贝将是面试官的考察重点:

  1. 是否将返回值声明为该类型的引用,并且在函数结束前返回实例自身的引用(即*this)。因为只有返回一个引用才可以进行连续赋值。如:str1 = str2 = str3
  2. 是否将传入参数声明为常量引用。如果传入参数不是引用,那么形参到实参会调用一次拷贝构造函数,引用可避免这样无谓消耗,提高代码效率。同时不能改变传入实例的状态,所以要将引用参数加上const关键字。
  3. 是否释放自己的内存。如果忘记在分配新内存之前释放自身已有的空间,程序将出现内存泄漏。
  4. 是否判断传入参数和当前实例(*this)是不是同一个实例。如果是同一个不进行赋值操作,直接返回。如果不实现判断就进行赋值,那么赋值前会释放自身空间,那么传入参数的内存也同时被释放,将再也找不到需要赋值的内容。

考虑异常安全

上面是实现使用于C++初级程序员,但对于C++高级程序员来说还需要考虑异常安全性。前面的实现中,我们在分配内存之前释放了m_data的内存,如果此时内存不足导致new char抛出异常,m_data将是一个空指针,这样非常容易导致程序崩溃。也就是说一旦在赋值运算符函数内部抛出一个异常,string的实例不再保持有效的状态,这就违背了安全性原则。

想要在赋值运算符函数中实现异常安全性,我们有两种方法。一个简单的方法是先用new分配新内容再释放原来空间,另一个更好的方法是先创建一个临时变量,再交换临时变量和原来的实例。代码实现如下:

string& operator = (const string& rhs) 
{
    if (this != &rhs) 
    {
        string tmp(rhs);
        char* p = tmp.m_data;
        tmp.m_data = m_data;
        m_data = p;
    }
    return *this;
}

在上述实现中,先创建一个临时变量tmp,接着将tmp.m_data和自身的m_data交换,由于tmp是一个局部变量,出了作用域就会调用析构函数将内存释放掉。如果临时变量调用构造函数时,由于内存不足抛出bad_alloc等异常,我们还没有修改原来实例的状态,因此实例是有效的,这保证了异常安全性。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • string经典实现
  • 赋值拷贝
  • 考虑异常安全
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档