String系列——基础实现

本节实现一个简单的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;
}
//对象末尾添加字符串s
MString& 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开始插入字符串s
MString& 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开始插入字符串s
MString& 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值