深入剖析C++中 String 类的模拟实现

目录

引言

一、基础框架搭建

成员变量与基本构造函数

析构函数

二、拷贝与赋值操作

深拷贝的拷贝构造函数

赋值运算符重载

三、字符串操作功能实现

获取字符串长度

字符串拼接

字符串比较

字符访问

四、迭代器相关实现(简单模拟)

迭代器类型定义

迭代器获取函数

五、总结


引言

在C++ 编程世界里, string 类是处理字符串的重要工具。标准库中的 string 类功能强大且使用便捷,但深入理解其底层实现原理,对提升C++ 编程能力大有裨益。今天,我们就来全面模拟实现一个功能较为完备的 String 类,涵盖构造、拷贝、赋值、析构以及丰富的字符串操作等功能。

一、基础框架搭建

成员变量与基本构造函数

cpp

class String {

private:

    char* _str; // 用于存储字符串



public:

    // 构造函数,默认构造空字符串

    String(const char* str = "") {

        if (str == nullptr) {

            // 若传入指针为空,视为错误情况,这里简单断言并处理

            assert(false);

            _str = new char[1];

            *_str = '\0';

            return;

        }

        // 计算字符串长度并分配内存

        size_t len = strlen(str);

        _str = new char[len + 1];

        // 复制字符串内容

        strcpy(_str, str);

    }



上述构造函数处理了传入正常字符串以及空指针的情况,为对象正确初始化字符串存储空间。

析构函数

cpp

    ~String() {

        // 释放字符串占用的动态内存

        if (_str) {

            delete[] _str;

            _str = nullptr;

        }

    }

析构函数负责在对象生命周期结束时,释放动态分配的内存,防止内存泄漏。

二、拷贝与赋值操作

深拷贝的拷贝构造函数

cpp

    // 拷贝构造函数,采用深拷贝方式

    String(const String& s) {

        size_t len = strlen(s._str);

        _str = new char[len + 1];

        strcpy(_str, s._str);

    }

深拷贝保证了新创建的对象拥有独立的字符串副本,与原对象互不干扰,避免了浅拷贝可能引发的资源共享和释放问题。

赋值运算符重载

cpp

    // 赋值运算符重载

    String& operator=(const String& s) {

        if (this!= &s) { // 避免自我赋值

            char* tmp = new char[strlen(s._str) + 1];

            strcpy(tmp, s._str);

            delete[] _str;

            _str = tmp;

        }

        return *this;

    }



该实现先为新值分配内存,复制内容后再释放原字符串内存,保证了赋值操作的正确性和安全性。同时处理了自我赋值的情况,防止错误发生。

三、字符串操作功能实现

获取字符串长度

cpp

    size_t size() const {

        return strlen(_str);

    }

通过调用标准库的 strlen 函数,返回当前字符串的长度。

字符串拼接

cpp

    String operator+(const String& s) const {

        size_t len1 = strlen(_str);

        size_t len2 = strlen(s._str);

        char* result = new char[len1 + len2 + 1];

        strcpy(result, _str);

        strcat(result, s._str);

        String newStr(result);

        delete[] result;

        return newStr;

    }

该函数实现了两个 String 对象的拼接操作。先计算拼接后字符串的总长度,分配足够内存,然后依次复制两个字符串内容,最后返回新的 String 对象。

字符串比较

cpp

    int compare(const String& s) const {

        return strcmp(_str, s._str);

    }

利用标准库的 strcmp 函数实现字符串的字典序比较,返回值小于0表示当前字符串小于传入字符串,等于0表示两者相等,大于0表示当前字符串大于传入字符串。

字符访问

cpp

    char& operator[](size_t index) {

        assert(index < strlen(_str));

        return _str[index];

    }

    const char& operator[](size_t index) const {

        assert(index < strlen(_str));

        return _str[index];

    }

提供了像数组一样通过下标访问字符的功能,同时区分了常量对象和非常量对象的访问方式,保证了操作的安全性和正确性。

四、迭代器相关实现(简单模拟)

迭代器类型定义

cpp

    typedef char* iterator;

    typedef const char* const_iterator;

定义了普通迭代器和常量迭代器类型,方便对字符串进行遍历操作。

迭代器获取函数

cpp

    iterator begin() {

        return _str;

    }

    iterator end() {

        return _str + strlen(_str);

    }

    const_iterator begin() const {

        return _str;

    }

    const_iterator end() const {

        return _str + strlen(_str);

    }

这些函数分别返回字符串起始和结束位置的迭代器,支持通过迭代器对字符串进行遍历,如:

cpp

String s("hello");

for (String::iterator it = s.begin(); it!= s.end(); ++it) {

    std::cout << *it;

}

完整代码

#define _CRT_SECURE_NO_WARNINGS
#include<assert.h>
#include<iostream>
#include<string>
using namespace std;
namespace ldg
{

    class string

    {

        

    public:
        /*friend ostream& operator<<(ostream& out, const string& s);

        friend istream& operator>>(istream& in, string& s);*/

        typedef char* iterator;

    public:
        /*string(const char* str="")
        {
            _size = strlen(str);
            _capacity = _size;
            strcpy(_str, str);
        }*/

        string(const char* str) 
        {
            _size = strlen(str);
            _capacity = _size;
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }
        string()
        {
            _size=0;
            _capacity=0;
            _str = new char[1];
            _str[0] = '\0';
        }

        string(const string& s)
        {
            
            _str = new char[s._capacity + 1];
            memcpy(_str,s._str,s._size+1);
            _size = s._size;
            _capacity = s._capacity;
        }
        /*string& operator=(const string& s)
        {
            if (*this != s)
            {
                delete[]_str;
                _str= new char[s._capacity + 1];
                strcpy(_str, s._str);
                _size = s._size;
                _capacity = s._capacity;
            
            }

            return *this;
        }*/

       /* string& operator=(const string& s)
        {
            if (*this != s)
            {
                string tmp(s);
                swap(tmp);
            }
           
            return *this;
        }*/

        string& operator=( string tmp)
        {           
            swap(tmp);           
            return *this;
        }

        ~string()
        {
            
             delete[]_str;
             _str = nullptr;
            _size = _capacity = 0;
        }



            //////////////////////////////////////////////////////////////

            // iterator

        iterator begin(){
            return _str;
        }

        iterator end(){
            return _str + _size;
        }

        iterator begin()const{
            return _str;
        }

        iterator end()const{
            return _str + _size;
        }


            /////////////////////////////////////////////////////////////

            // modify
        void emcapacity(int newcapacity)
        {
            if (newcapacity <= _capacity)return;
            char* newstr = new char[newcapacity];
            strcpy(newstr, _str);
            delete[]_str;
            _str = newstr;
            _capacity = newcapacity;
        }
        void reserve(size_t n)
        {
            if (n > _capacity)
            {
                char* newstr = new char[n];
                strcpy(newstr, _str);
                delete[]_str;
                _str = newstr;
                _capacity = n;
            }
            
        }

        void push_back(char c)
        {
            if (_size >= _capacity)
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }
            _str[_size++] = c;
            _str[_size] = '\0';
        }

        string& operator+=(char c)
        {
            push_back(c);
            return *this;
        }

        void append(const char* str)
        {
            size_t len = strlen(str);
            if (len + _size >= _capacity)
            {
                reserve(_capacity + len);
            }
            strcpy(_str+_size, str);
            _size += len;
        }

        string& operator+=(const char* str)
        {
            append(str);
            return *this;
        }

        void clear()
        {
            _size = 0;
            _str[0] = '\0';
        }

        void swap(string& s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }

        const char* c_str()const
        {
            return _str;
        }



        /////////////////////////////////////////////////////////////

        // capacity

        size_t size()const
        {
            return _size;
        }

        size_t capacity()const
        {
            return _capacity;
        }

        bool empty()const
        {
            return _size == 0;
        }

        void resize(size_t n, char c = '\0')
        {
            if (n <= _size)
            {
                _size = n;
                _str[_size] = '\0';
            }
            else {
                reserve(n);
                /*if (n > _capacity)
                {
                    emcapacity(n);
                }*/
                for (size_t i = _size;i < n;i++)_str[i] = c;
                _size = n;
                _str[_size] = '\0';
            }
        }

       /* void reserve(size_t n)
        {
            if (n > _capacity)
            {
                emcapacity(n);
            }
        }*/



        /////////////////////////////////////////////////////////////

        // access

        char& operator[](size_t index)
        {
            assert(index < strlen(_str));
            return _str[index];
        }

        const char& operator[](size_t index)const
        {
            assert(index < strlen(_str));
            return _str[index];
        }



        /////////////////////////////////////////////////////////////

        //relational operators

        bool operator<(const string& s)
        {
           /* size_t i1;
            size_t i2;
            while(i1 < _size&& i2 < s._size)
            {
                if (_str[i1] < s._str[i2])return true;
                if (_str[i1] > s._str[i2])return false;
                else {
                    i1++, i2++;
                }
            }
            return i1 == _size && i2 != s._size;*/
            bool ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
            return ret == 0 ? _size < s._size : ret;
        }

        bool operator==(const string& s)
        {
            return _str&&s._str&&memcmp(_str, s._str, _size)==0;
        }

        bool operator<=(const string& s)
        {
            return *this < s || *this == s;
        }

        bool operator>(const string& s)
        {
            return !(*this <= s);
        }

        bool operator>=(const string& s)
        {
            return !(*this<s);
        }

        bool operator!=(const string& s)
        {
            return !(*this == s);
        }



        // 返回c在string中第一次出现的位置

        size_t find(char c, size_t pos = 0) const
        {
            assert(pos <= _size);
            for (size_t i = pos;i < _size;i++)
            {
                if (_str[i] == c)
                {
                    return i;
                }
            }
            return std::string::npos;
        }

        // 返回子串s在string中第一次出现的位置

        size_t find(const char* s, size_t pos = 0) const
        {
            assert(pos <= _size);
            const char* ret = std::strstr(_str+pos, s);
            if (ret)return ret - _str;
            return std::string::npos;
        }

        // 在pos位置上插入字符c/字符串str,并返回该字符的位置

        string& insert(size_t pos,size_t n, char c)
        {
            assert(pos <= _size);
            
            if (_size + n >= _capacity)reserve(_size + n);
            int end = _size;
            while (end >= (int)pos)
            {
                _str[end+n] = _str[end + 1];
                end--;
            }
            for (int i = 0;i < n;i++)
            {
                _str[pos + i] = c;
            }
            _size += n;
           
            return *this;
        }

        string& insert(size_t pos, const char* str)
        {
            assert(pos <= _size);
            size_t len = strlen(str);
            if (_size+len >= _capacity) reserve(_size + len+1);
            int end = _size;
            while (end >= (int)pos)
            {
                _str[end + len] = _str[end];
                end--;
            }
            for (int i = 0;i < len;i++)
            {
                _str[pos + i] = str[i];
            }
            _size += len;

            return *this;
        }



        // 删除pos位置上的元素,并返回该元素的下一个位置

        string& erase(size_t pos, size_t len)
        {
            if (pos > _size)return *this;
            if (pos + len > _size)len = _size - pos;
            for (size_t i = pos;i < _size - len;i++)_str[i] = _str[i + len];
            _size -= len;
            _str[_size] = '\0';
            return *this;
        }

    private:
        size_t _size;
        size_t _capacity;

        char* _str;  

    };

    ostream& operator<<(ostream& out, const ldg::string& s)
    {
        for (int i = 0;i < s.size();i++)
        {
            out << s[i];
        }
        return out;
    }
    
    
//istream& operator>>(istream& in, string& s)
//{
//    s.clear();
//    char ch = in.get();
//    while (ch == ' ' || ch == '\0')
//    {
//        ch = in.get();
//    }
//    char buff[128];
//    int i = 0;
//    while (ch != ' ' && ch != '\0')
//    {
//       buff[i++] = ch;
//        if (i == 127)
//        {
//            buff[i] = '\0';
//            s += buff;
//            i = 0;
//        }
//        ch = in.get();
//    }
//   if (i != 0)
//    {
//        
//        s += buff;
//    }
//    return in;
//}
    std::istream& operator>>(std::istream& in, ldg::string& s) {
        s.clear();
        std::string temp;
        std::getline(in, temp);
        size_t start = 0;
        while (start < temp.size() && (temp[start] == ' ' || temp[start] == '\0')) {
            start++;
        }
        size_t i = start;
        while (i < temp.size() && temp[i] != ' ' && temp[i] != '\0') {
            i++;
        }
        std::string subStr = temp.substr(start, i - start);
        // 将std::string转换为const char*
        s += subStr.c_str();
        return in;
    }

};

五、总结

通过以上一系列的实现,我们构建了一个功能相对丰富的 String 类模拟版本。从基本的对象生命周期管理(构造、析构),到关键的拷贝与赋值操作,再到多样化的字符串操作以及简单的迭代器支持,每一个部分都精心设计,确保正确性和高效性。理解这些底层实现原理,不仅能让我们更好地使用标准库中的 string 类,还能在面对复杂字符串处理需求时,具备更强的代码设计和优化能力。在实际编程中,我们可以根据具体场景进一步扩展和完善这个 String 类,使其发挥更大的作用。

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值