String系列——基础实现

来源:互联网 发布:学php的就业面 编辑:程序博客网 时间:2024/06/07 07:42

本节实现一个简单的string类,以方便后面的string系列能够顺利进行,设计string系列旨在学习和理解一些底层技术,比如COW、内存池等等,另一方面学习语言的基本功,同时也可以加深对string类的理解。
代码可能很长,但是学习的最好办法就是阅读代码。

类定义

这里定义了string类的一些基本函数

class MString{public:    //构造函数    MString():m_len(0),m_str(0){}    MString(const MString& s);    MString(const char* s);    MString(size_t n);    ~MString();    //赋值操作符,实现对象赋值    MString& operator=(const MString& s);    //赋值操作符,实现字符串赋值    MString& operator=(const char* s);    //转换成c类型字符串    const char* c_str(){ return m_str; }    //判断字符是否为空    bool empty(){ return m_len>0? true:false; }    //返回字符串长度    size_t length(){ return m_len; }    //返回字符串大小    size_t size(){return m_len;}    //删除pos开始的n个字符,不能为左值    const MString& erase(size_t pos, size_t n);    //对象末尾添加字符串s    MString& append(const MString& s);    //返回从pos开始的n个字符    MString substring(size_t pos,size_t n);    //从位置pos开始插入字符串s    MString& insert(size_t pos,const MString& s);    MString& insert(size_t pos,const char* s);    //把字符串s中从pos开始的n个字符返回赋给当前字符串    MString& assign(const MString& s, size_t,size_t);    MString& assign(const char* s, size_t,size_t);private:    size_t m_len;    char* m_str;};

实现

#include "mstring.h"MString::MString(const MString& s){    m_len = s.m_len;    m_str = new char[m_len+1];    strcpy(m_str,s.m_str);}MString::MString(const char* s){    if (s == NULL)    {        return;    }    m_len = strlen(s);    m_str = new char[m_len+1];    strcpy(m_str,s);}MString::MString(size_t n){    m_len = n;    m_str = new char[n+1];}MString::~MString(){    delete[] m_str;    m_len = -1;}//赋值操作符,深拷贝MString& MString::operator=(const MString& s){    if (strcmp(this->m_str, s.m_str) == 0) //两个字符串相等,则直接返回。    {        return *this;    }    if (s.m_len > 0) //如果两个字符串不想等,且s中存在元素。    {        if (m_len > 0)        {            delete[] m_str;        }        this->m_len = s.m_len;        m_str = new char[m_len + 1];        strcpy(m_str, s.m_str);        m_str[m_len] = '\0';//strcpy已近复制了'\0'    }    //若s为空,直接返回本身,若不为空返回深拷贝后的自己    return *this;}//字符串赋值MString& MString::operator=(const char* s){    if (s == NULL)    {        return *this;    }    if (this->m_len > 0)    {        delete[] m_str;    }    this->m_len = strlen(s);    this->m_str = new char[m_len+1];    strcpy(m_str,s);    m_str[m_len] = '\0'; //strcpy已近复制了'\0'    return *this;}//删除pos开始的n个字符,pos位置以0开开始const MString& MString::erase(size_t pos, size_t n){    assert(pos < m_len && pos > 0 && n > 0 && n < m_len);    if (pos + n < m_len) //如果删除的长度在len范围内    {        int i;        int move_num = m_len - pos - n ;        for (i = 0;i < move_num;i++)        {            m_str[pos+i] = m_str[pos + n + i];        }        m_str[pos + i] = '\0';    }    else //删除的长度超过m_len    {        m_str[pos] = '\0';    }    return *this;}//对象末尾添加字符串sMString& MString::append(const MString& s){    if (s.m_len < 1)    {        return *this;    }    m_len += s.m_len;    char* tmp_str = new char[m_len +1];    strcpy(tmp_str,m_str);    strcat(tmp_str,s.m_str);    tmp_str[m_len] = '\0';    delete[] m_str;    m_str = tmp_str;    return *this;}//返回从pos开始的n个字符MString MString::substring(size_t pos,size_t n){    assert(pos < m_len && pos > 0 && n > 0 && n < m_len);    MString* new_string = new MString(n);    if (pos + n < m_len) //截取长度在字符串范围内    {        while(n--)        {            new_string->m_str[n] = this->m_str[pos + n];        }        new_string->m_str[new_string->m_len] = '\0';    }    else //截取长度在字符串之外    {        strcpy(new_string->m_str, &this->m_str[pos]);        new_string->m_str[this->m_len - pos]='\0';    }    return *new_string;}//从位置pos开始插入字符串sMString& MString::insert(size_t pos,const MString& s){    assert(pos >= 0 && pos <= m_len);    m_len += s.m_len;    char* tmp_str = new char[m_len +1];    //赋值新的字符串中    strncpy(tmp_str,m_str,pos);    tmp_str[pos] = '\0';    strcat(tmp_str,s.m_str);    strcat(tmp_str,&m_str[pos]);    tmp_str[m_len] = '\0';    //释放原字符串    delete[] m_str;    m_str = tmp_str;    return *this;}//从位置pos开始插入字符串sMString& MString::insert(size_t pos,const char* s){    assert(pos >= 0 && pos <= m_len);    m_len += strlen(s);    char* tmp_str = new char[m_len +1];    //赋值新的字符串中    strncpy(tmp_str,m_str,pos);    tmp_str[pos] = '\0'; //注意这里要加结束符,否则strcat会一直找到'\0'时才开始连接。    strcat(tmp_str,s);    strcat(tmp_str,&m_str[pos]);    tmp_str[m_len] = '\0';    //释放原字符串    if (m_str != NULL)    {        delete[] m_str;    }    m_str = tmp_str;    return *this;}//把字符串s中从pos开始的n个字符返回赋给当前字符串MString& MString::assign(const MString& s, size_t pos,size_t n){    assert(s.m_len >= pos + n && pos >= 0 && n>0);    if (m_len == 0 && m_str == 0)    {        m_len = n;        m_str = new char[m_len +1];        strncpy(m_str, &s.m_str[pos],n);        m_str[m_len] = '\0';    }    else    {        m_len = n;        char* tmp_str = new char[m_len +1];        strncpy(tmp_str, &s.m_str[pos],n);        tmp_str[m_len] = '\0';        delete[] m_str;        m_str = tmp_str;    }    return *this;}MString& MString::assign(const char* s, size_t pos,size_t n){    size_t slen = strlen(s);    assert(slen >= pos + n && pos >= 0 && n>0);    if (m_len == 0 && m_str == 0)    {        m_len = n;        m_str = new char[m_len +1];        strncpy(m_str, &s[pos],n);        m_str[m_len] = '\0';    }    else    {        m_len = n;        char* tmp_str = new char[m_len +1];        strncpy(tmp_str, &s[pos],n);        tmp_str[m_len] = '\0';        delete[] m_str;        m_str = tmp_str;    }    return *this;}

其中,某些方法是参考vector实现思想写的。下一节,操作符重载总结(http://blog.csdn.net/z702143700/article/details/47166437)

2015-09-17修改:看了剑指offer后发现,自己 写代码还是很低级的。拿operator=来说。

实现中,先delete 掉m_str,再申请一段内存的时候,如果内存不足,new抛出异常,此时m_str就变成了一个空指针。这样很容易导致程序崩溃。

要实现这种异常安全性,必须先new到内存后再去释放。剑指offer中给了一个很好的解法:先创建一个临时实例,再交换临时和原来实例。

MString& MString::operator=(const MString& s){    if (&s != this)    {        MString* tms = new MString(s);        char* ptmp = tms->m_str;        tms->m_str = this->m_str;        this->m_str = ptmp;    }    return *this;}
0 0
原创粉丝点击