C++ STL - string 成员函数 + 模拟实现

目录

string - 构造函数

 简介:

分析:

验证:

string - 析构函数,重载赋值运算符

​编辑分析:

模拟实现string构造,拷贝构造,重载赋值运算符,析构

构造: 

拷贝构造+重载赋值运算符

传统写法:

现代写法:

string - Element access:

前言:

析:

实现:

string - 迭代器iterator

简介:

介绍:

使用:

string - 大小,容量函数

string - 修改string操作

string - 其余操作

完整tiny string


string - 构造函数

 简介:

string构造函数重载的形式比较多样,最常用的是1. 默认构造函数  2. 拷贝构造函数  4. 以C字符串-const char* 为参数的构造函数。 
其他的用的并不多。比如,string从pos位置开始的len个,C字符串的前n个,n个char,还有迭代器范围版本。

void test1()
{
    string s0("12345abc");  // string(const char* s);
    string s1;          // string();
    string s2(s0);    // 拷贝构造,string(const string& s);
    string s3 = s0;   // 拷贝构造,C方式的拷贝构造。
    string s4 = "aaaaa";
}

分析:

s2 为典型的拷贝构造,s3为C语言风格的拷贝构造。 值得注意的是s4,这里因为string(const char*) 版本构造函数不是explicit的,所以"aaaaa"隐式类型转换为(构造)string对象,再拷贝构造s4,编译器对此情况优化为直接进行构造。

验证:

当explicit string(const char* s); 时

解除explicit后,如果在string(const char* s)构造函数和拷贝构造中加上输出语句,运行这个构造代码后,发现并没有拷贝构造的输出语句,即编译器对此优化为了直接构造。

string - 析构函数,重载赋值运算符

分析:

比较简单,operator=重载了多个版本,注意区分赋值和构造的情况。析构函数在对象销毁时自动调用,用于回收清理对象申请的空间。

模拟实现string构造,拷贝构造,重载赋值运算符,析构

数据成员只有这几个。

    private:
        char* _str;
        size_t _size;
        size_t _capacity;
    public:
        const static size_t npos = -1;

构造: 

(都是class string的成员函数)

//        string() : _size(0),_capacity(0),_str(new char[1])
//        {
//            _str[0] = '\0';
//        }
        string(const char* str = "")
        {
            _size = strlen(str);  // strlen不包含\0,计算的是有效字符个数。
            _capacity = _size;
            _str = new char[_capacity+1];

            strcpy(_str, str);  // strcpy会拷贝'\0'
        }
        string(size_t n, char c)
        : _size(n),_capacity(n),_str(new char[n+1])
        {
            for(size_t i = 0; i < n; ++i)
                _str[i] = c;
            _str[n] = '\0';
        }

没有全部实现,实现了几个常用的,明确size和capacity的意义,利用C语言库函数和new即可简单实现。

拷贝构造+重载赋值运算符

传统写法:

        string(const string& s)
        : _size(s._size),_capacity(s._capacity),_str(new char[s._capacity+1])
        {
            strcpy(_str,s._str);
        }
//         s1 = s3;
        string& operator=(const string& s)
        {
            if(this != &s)
            {
                delete[] this->_str;
                _str = new char[s._capacity+1];
                _size = s._size;
                _capacity = s._capacity;
                strcpy(_str,s._str);
            }
            return *this;
        }
//         略微改进了一点,考虑到如果new开空间失败,不破坏原对象。
        string& operator=(const string& s)
        {
            if(this != &s)
            {
                char* tmp = new char[s._capacity+1];
                strcpy(tmp,s._str);
                delete[] this->_str;
                _str = tmp;
                _size = s._size;
                _capacity = s._capacity;
            }
            return *this;
        }

现代写法:

        void swap(string& tmp)
        {
            std::swap(_str,tmp._str);
            std::swap(_size,tmp._size);
            std::swap(_capacity,tmp._capacity);
        }
        // 现代写法: 老板思维
        string(const string& s)    // string s1(s2)
        :_size(0),_capacity(0),_str(nullptr)    // 如果不这样,this的_str是随机值,tmp析构时,delete[]随机值非法。
        {
            string tmp(s._str);   // 调用string(const char*) 构造函数
            this->swap(tmp);
        }
        string& operator=(const string& s)
        {
            if (this != &s) {
//                string tmp(s);
                string tmp(s._str);
                swap(tmp);
            }
            return *this;
        }
        // 最简版
        string& operator=(string s)
        {
            swap(s);
            return *this;
        }

        string& operator=(const char* s) {
            string tmp(s);
            this->swap(tmp);
            return *this;
        }
        ~string()
        {
            delete[] _str;
            _str = nullptr;
            _size = _capacity = 0;
        }

1. 拷贝构造的现代写法:利用string(const char* s)构造出临时对象tmp,其实就是用实参string拷贝一份,然后交换*this 和 tmp,因为这里的*this刚刚初始化完,数据成员都是随机值,如果和tmpswap之后,tmp析构时,数据成员是随机值会报错,所以利用初始化列表将*this数据成员简单初始化以下,再与tmp交换。

2. 对于swap函数,这里利用的是string的成员函数swap,上方有实现。原因有2:a、标准库中的std::swap()对于交换的两个对象,是先拷贝构造一份,再赋值两次,对于深拷贝的自定义类型来说,效率较低。  b、std::swap()会调用交换对象的类型的拷贝构造和operator=,我们在实现的拷贝构造中使用std::swap就会造成无限递归。  所以必须使用strin

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值