string及其模拟实现

标准库中的string类

        string 类

        2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作 单字节字符字符串的设计特性。

        3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信 息,请参阅basic_string)。

        4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits 和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。

        5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个 类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结

1. string是表示字符串的字符串类

2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string string;

4. 不能操作多字节或者变长字符的序列。

在使用string类时,必须包含#include头文件以及using namespace std;

string类的常用接口说明(注意下面我只讲解最常用的接口)

 1 string类对象的常见构造:

2. string类对象的容量操作

1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。

2. clear()只是将string中有效字符清空,不改变底层空间大小。

3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变。

4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。

3. string类对象的访问及遍历操作

注意:end() 是获取最后一个字符的下一个位置的迭代器

void Teststring()
{
 string s1("hello Bit");
 const string s2("Hello Bit");
 cout<<s1<<" "<<s2<<endl;
 cout<<s1[0]<<" "<<s2[0]<<endl;
 
 s1[0] = 'H';
 cout<<s1<<endl;
 
 // s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改
}
 
void Teststring()
{
 string s("hello Bit");
 // 3种遍历方式:
 // 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
 // 另外以下三种方式对于string而言,第一种使用最多
 // 1. for+operator[]
 for(size_t i = 0; i < s.size(); ++i)
 cout<<s[i]<<endl;
 
 // 2.迭代器
 string::iterator it = s.begin();
 while(it != s.end())
 {
 cout<<*it<<endl;
 ++it;
 }
 
 string::reverse_iterator rit = s.rbegin();
 while(rit != s.rend())
 cout<<*rit<<endl;
 
 // 3.范围for
 for(auto ch : s)
 cout<<ch<<endl;
}

4. string类对象的修改操作

        

void Teststring()
{
 string str;
 str.push_back(' '); // 在str后插入空格
 str.append("hello"); // 在str后追加一个字符"hello"
 str += 'w'; // 在str后追加一个字符'w' 
 str += "orld"; // 在str后追加一个字符串"rold"
 cout<<str<<endl;
 cout<<str.c_str()<<endl; // 以C语言的方式打印字符串
 
 // 获取file的后缀
 string file1("string.cpp");
 size_t pos = file.rfind('.');
 string suffix(file.substr(pos, file.size()-pos));
 cout << suffix << endl;
 
 // npos是string里面的一个静态成员变量
 // static const size_t npos = -1;
 
 // 取出url中的域名
 string url("http://www.cplusplus.com/reference/string/string/find/");
 cout << url << endl;
 size_t start = url.find("://");
 if (start == string::npos)
 {
 cout << "invalid url" << endl;
 return;
 }
 start += 3;
 size_t finish = url.find('/', start);
 string address = url.substr(start, finish - start);
 cout << address << endl;
 
 // 删除url的协议前缀
 pos = url.find("://");
 url.erase(0, pos+3);
 cout<<url<<endl;
}

注意:

        1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般 情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

        2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

5. string类非成员函数

relational operators :>,>=,<,<=,!=,

gertline():可输入带空格的,

string类的模拟实现

        经典的string类问题

class string
2  {
3  public:
4      /*string()
5          :_str(new char[1])6          {*_str = '\0';}
7      */
8      //string(const char* str = "\0") 错误示范
9      //string(const char* str = nullptr) 错误示范
10      string(const char* str = "")
11      {
12          // 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下
13          if(nullptr == str)
14          {
15              assert(false);
16              return;
17          }
18          
19          _str = new char[strlen(str) + 1];
20          strcpy(_str, str);
21      }
22      
23      ~string()
24      {
25          if(_str)
26          {
27              delete[] _str;
28              _str = nullptr;
29          }
30      }
31      
32  private:
33      char* _str;
34  };
35
36  // 测试
37  void Teststring()
38  {
39      string s1("hello bit!!!");
40      string s2(s1);
41  }

说明:上述string类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以 当继续对资源进项操作时,就会发生发生了访问违规。要解决浅拷贝问题,C++中引入了深拷贝。

深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

传统版写法的string类

1  class string
2  {
3  public:
4      string(const char* str = "")
5      {        
6          // 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下
7          if(nullptr == str)
8          {
9              assert(false);
10              return;
11          }
12          
13          _str = new char[strlen(str) + 1];
14          strcpy(_str, str);
15      }
16      
17      string(const string& s)
18          : _str(new char[strlen(s._str)+1])
19      {
20              strcpy(_str, s._str);
21      }
22      
23      string& operator=(const string& s)
24      {
25          if(this != &s)
26          {
27              char* pStr = new char[strlen(s._str) + 1];
28              strcpy(pStr, s._str);
29              delete[] _str;
30              _str = pStr;
31          }
32          
33          return *this;
34      }
35      
36      ~string()
37      {
38          if(_str)
39          {
40              delete[] _str;
41              _str = nullptr;
42          }
43      }
44      
45  private:
46      char* _str;
47  };

现代版写法的string类

1 class string
2  {
3  public:
4      string(const char* str = "")
5      {
6          if(nullptr == str) 
7           str = "";
8         
9          _str = new char[strlen(str) + 1];
10          strcpy(_str, str);
11      }
12      
13      string(const string& s)
14          : _str(nullptr)
15      {
16          string strTmp(s._str);
17          swap(_str, strTmp._str);
18      }
19      
20      // 对比下和上面的赋值那个实现比较好
21      string& operator=(string s)
22      {
23          swap(_str, s._str);   
24          return *this;
25      }
26      
27      /*
28      string& operator=(const string& s)
29      {
30          if(this != &s)
31          {
32             string strTmp(s);
33             swap(_str, strTmp._str);
34          }
35          
36          return *this;
37      }
38      */
39     
40      ~string()
41      {
42          if(_str)
43          {
44              delete[] _str;
45              _str = nullptr;
46          }
47      }
48      
49  private:
50      char* _str;
51  };

写时拷贝

写时拷贝,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

string类的模拟实现

namespace bit
 {
     class string
     {
     public:
         typedef char* iterator;
     public:
         string(const char* str = "")
         {
             _size = strlen(str);
             _capacity = _size;
             _str = new char[_capacity+1];
             strcpy(_str, str);
         }
 
         string(const string& s)
             : _str(nullptr)
             , _size(0)
             , _capacity(0)
         {
             string tmp(s._str);
             this->swap(tmp);
         }
 
         string& operator=(string s)
         {
             this->Swap(s)
            return *this;
         }
 
         ~string()
         {
             if (_str)
             {
                 delete[] _str;
                 _str = nullptr;
             }
         }
 
 /
 // iterator
         iterator begin() {return _str;}
         iterator end(){return _str + _size;}
 
 /
 // modify
         void push_back(char c)
         {
             if (_size == _capacity)
             Reserve(_capacity*2);
 
             _str[_size++] = c;
             _str[_size] = '\0';
         }
 
         string& operator+=(char c)
         {
             PushBack(c);
             return *this;
         }
 
 // 作业实现
         void append(const char* str);
         string& operator+=(const char* str); 
 
         void clear()
         {
             _size = 0;
             _str[_size] = '\0';
         }
 
         void swap(string& s)
         {
             swap(_str, s._str);
             swap(_size, s._size);
             swap(_capacity, s._capacity);
         }
 
         const char* c_str()const
         {
             return _str;
         }
 
 /
 // capacity
         size_t size()const
         size_t capacity()const
         bool empty()const
 
         void resize(size_t newSize, char c = '\0')
         {
             if (newSize > _size)
             {
 // 如果newSize大于底层空间大小,则需要重新开辟空间
                 if (newSize > _capacity)
                 {
                     Reserve(newSize);
                 }
 
                 memset(_str + _size, c, newSize - _size);
             }
 
             _size = newSize;
             _str[newSize] = '\0';
         }
 
         void reserve(size_t newCapacity)
         {
 // 如果新容量大于旧容量,则开辟空间
             if (newCapacity > _capacity)
             {
                 char* str = new char[newCapacity + 1];
                 strcpy(str, _str);
 
 // 释放原来旧空间,然后使用新空间
                 delete[] _str;
                 _str = str;
                 _capacity = newCapacity;
             }
         }
 
 
 // access
         char& operator[](size_t index)
         {
             assert(index < _size);
             return _str[index];
         }
 
         const char& operator[](size_t index)const
         {
             assert(index < _size);
             return _str[index];
        }
 
 

 bool operator<(const string& s);
 bool operator<=(const string& s);
 bool operator>(const string& s);
 bool operator>=(const string& s);
 bool operator==(const string& s);
 bool operator!=(const string& s);
 
 // 返回c在string中第一次出现的位置
         size_t find (char c, size_t pos = 0) const;
 // 返回子串s在string中第一次出现的位置
         size_t find (const char* s, size_t pos = 0) const;
 
 // 在pos位置上插入字符c/字符串str,并返回该字符的位置
         string& insert(size_t pos, char c);
         string& insert(size_t pos, const char* str);
 
 // 删除pos位置上的元素,并返回该元素的下一个位置
         string& erase(size_t pos, size_t len);
 
 private:
     friend ostream& operator<<(ostream& _cout, const bit::string& s);
     friend istream& operator>>(istream& _cin, bit::string& s);
 private:
     char* _str;
     size_t _capacity;
     size_t _size;
     };
 }
 
 ostream& bit::operator<<(ostream& _cout, const bit::string& s)
 {
 // 不能使用这个
 //cout << s._str;
     for(size_t i = 0; i < s.size(); ++i)
 {
     _cout<<s[i];
 }
     return _cout;
 }
 
 ///对自定义的string类进行测试
 void TestBitstring()
 {
     bit::string s1("hello");
     s1.push_back(' ');
     s1.push_back('b');
     s1.append(1, 'i');
     s1 += 't';
     cout << s1 << endl;
     cout<<s1.size()<<end1;
     cout<<s1.capacity()<<endl;
//利用迭代器打印string中的元素
    sting::iterator it=s1.begin();
    whiel(it!+s1.end())
    {
        cout<<*it<<" ";
        ++it;

    }
    cout<<endl;

//这里可以看到一个类只要支持的基本的iterator,就支持范围for
    for(auto ch:s1)
        cout<<ch<<" ";
    cout<<end1;
}  
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值