C++——模拟实现string

打印 上一主题 下一主题

主题 778|帖子 778|积分 2334

1.再谈string

string为什么要被计划成模板?日常使用string好像都是char*,char*不够使用吗,为什么要计划成模板呢?
1.1 关于编码

  1. //计算机的存储如何区分呢?
  2. int main()
  3. {
  4.         //比如在C语言中,有整型
  5.         //如果是有符号的话,设计了原、反、补码
  6.         //负数要存储它的补码,方便运算
  7.         //浮点数要设计到存精度等问题
  8.         int a = 0;
  9.         return 0;
  10. }
复制代码
那么怎样去更好地表示其它的各种文字呢?
1.2 ASCII编码

ASCII_百度百科 (baidu.com)

   ASCII编码表示美国信息技能的编码,在一开始计划时,只思量了存储美国的文字信息
  
  内存中存储字母时,不会把字母存储进去,内存中都是0、1组合的二进制代码,以是,要在二进制代码代表的值符号间,建立一个一一映射对应的关系,这个关系表,一般称为编码表
  
ASCII表就是美国的文字所建立的拥有一一映射对应的关系的编码表
  
  1. int main()
  2. {       
  3.     char str[] = "hello world";
  4.         return 0;
  5. }
  6. //要显式时,发现它是字符串,会去查它的编码表
复制代码


1.3 unicode

同一码_百度百科 (baidu.com)

计算机向全世界推广后,不单单需要能够表示汉字,还需要能够表示其他国家的文字符号,有些国家的文字比较简单,有些可能比汉字还要复杂。因此,计划出了万国码。

   以是要表示汉字,也要使得每个汉字都对应一个数字
那怎样表示汉字呢?也是用一个byte表示一个汉字吗?
要知道1个byte才8个bit位,如许的话才只能表示256个汉字,显然不能很好地表示
  
  方案有三种
  1.3.1 UTF-8


  1. int main()
  2. {
  3.         //UTF-8   主流使用,一个值可能在多个字节上面存储
  4.         //但windows喜欢使用gbk,linux喜欢使用UTF-8
  5.         char str1[] = "hello world";//12byte
  6.         char str2[] = "工大";//5byte
  7.         char str3[] = "工大 hello";//可以混着使用,11byte
  8.         cout << sizeof(str1) << endl;
  9.         cout << sizeof(str2) << endl;
  10.         cout << sizeof(str3) << endl;
  11.         char* a = str3;
  12.         cout << *a << endl;
  13.     return 0;
  14. }
复制代码


   UTF-8的缺点:变长,意味着辨认比较复杂,太差异一,好比做屏蔽时;
或是有些情况下文字不需要兼容ASCII,这时就要使用其它方式了
  
1.3.2 UTF-16


1.3.3 UTF-32


1.4计划成模板的原因


  1. int main()
  2. {
  3.         //为了更好地兼容这两种编码,类型进行了延申
  4.         //C++11之前,设计出了宽字节,一个char占据2byte
  5.     //用来更好地表示其它的编码,比如UTF-16
  6.        
  7.     wchar_t ch1;        //宽字节
  8.         cout << sizeof(ch1) << endl;//2byte
  9.         char16_t ch2;
  10.         char32_t ch3;
  11.         cout << sizeof(ch2) << endl;//2byte
  12.         cout << sizeof(ch3) << endl;//4byte
  13.     return 0;
  14. }
复制代码
这也是string要计划成模板的原因,它可以传差异的模板参数,好比char16_t、char32_t等,可以实用差异的编码,表示更多国家的差异的文字。

1.5作甚乱码

   好比某文字默认使用UTF-8来存储,但表现时没有使用对于的编码表来查找,就会出现乱码。
存储格式和表明方式没有对应起来
  


1.6GBK

GBK字库_百度百科 (baidu.com)
固然unicode是全世界的编码,但它未必非常适合汉字的表达。GBK是中国创造的、更适合汉字表达的编码表。

  1. int main()
  2. {
  3.         char s1[] = "你好!!";
  4.         s1[0]++;//陪
  5.         s1[0]++;
  6.         s1[0]++;
  7.         s1[0]++;
  8.         s1[3]++;//耗
  9.         s1[3]++;
  10.         s1[3]++;//号
  11.         //在净网行动中非常有用
  12.         //黑名单词库
  13.     return 0;
  14. }
复制代码
2.模拟实现string

2.1无参的构造和析构

  1. //string.h
  2. #pragma once
  3. namespace jxy
  4. {
  5.         class string
  6.         {
  7.         public:
  8. //初始化的顺序是按照声明的顺序,而不是初始化列表出现的顺序
  9.                 string(const char* str)
  10.                         :_size(strlen(str))
  11.                         ,_capacity(_size)//capacity一般不算'\0'
  12.                 {
  13.                         _str = new char[_capacity+1];//开空间时要多开一个
  14.                         strcpy(_str, str);//strcpy会把'\0'也拷贝过去
  15.                 }
  16.                 ~string()
  17.                 {
  18.                         delete[] _str;
  19.                         _str = nullptr;
  20.                         _size = _capacity = 0;
  21.                 }
  22.                 const char* c_str() const
  23.                 {
  24.                         return _str;
  25.                 }
  26.         private:
  27.                 char* _str;
  28.                 size_t _size;
  29.                 size_t _capacity;
  30.         };
  31.         void test_string1()
  32.         {
  33.                 string s1("hello world");
  34.                 cout << s1.c_str() << endl;
  35.         }
  36. }
  37. //Test.cpp
  38. #define _CRT_SECURE_NO_WARNINGS
  39. #include<iostream>
  40. //这里间接包了string的相关函数
  41. //#include"string.h"
  42. //注意,如果写在这里可能会报错,因为std的展开在下面
  43. //编译器为了追求编译速度,只会向上查找
  44. using namespace std;
  45. #include"string.h"//编译器实际编译时是没有.h的,在预处理阶段就在.c或者.cpp展开了
  46. int main()
  47. {
  48.         jxy::test_string1();
  49.         return 0;
  50. }
复制代码
2.2单参数的构造

错误示范:
  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 string()
  7.                         :_str(nullptr)
  8.                         ,_size(0)
  9.                         ,_capacity(0)
  10.                 {}
  11.         };
  12.         void test_string1()
  13.         {
  14.                 string s2;
  15.                 cout << s2.c_str() << endl;
  16.                
  17.         //这样会崩溃
  18.                 //char*有个特点,自定义类型识别会以字符串去识别,会直接去解引用,遇到'\0'才终止
  19.                 //这里空指针解引用就会出错
  20.                 std::string s1;
  21.                 cout << s1.c_str() << endl;//库里面的是没问题的
  22.         }
  23. }
复制代码
正确写法1:
  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 string()
  7.                         :_str(new char[1]{'\0'})//这里需要开空间
  8.                         ,_size(0)
  9.                         ,_capacity(0)
  10.                 {
  11.                         //_str[0] = '\0';//或者在这里初始化
  12.                 }
  13.         };
  14.         void test_string1()
  15.         {
  16.                 string s2;
  17.                 cout << s2.c_str() << endl;
  18.         }
  19. }
复制代码
正确写法2:在无参构造处给上缺省参数
  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 //const char* str="\0" 这样写不够规范
  7.                  
  8.                 string(const char* str="")//C语言规定常量字符串后面默认有'\0'
  9.                         :_size(strlen(str))
  10.                         ,_capacity(_size)
  11.                 {
  12.                         _str = new char[_capacity+1];
  13.                         strcpy(_str, str);
  14.                 }
  15.         };
  16.         void test_string1()
  17.         {
  18.                 string s2;
  19.                 cout << s2.c_str() << endl;
  20.         }
  21. }
复制代码
2.3 size、capacity和c_str

  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 size_t capacity() const
  7.                 {
  8.                         return _capacity;
  9.                 }
  10.                 size_t size() const
  11.                 {
  12.                         return _size;
  13.                 }
  14.                 const char* c_str() const
  15.                 {
  16.                         return _str;
  17.                 }
  18.         };
  19.         private:
  20.                 char* _str;
  21.                 size_t _size;
  22.                 size_t _capacity;
  23. }
复制代码
2.4遍历数组

2.4.1[]

  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 char& operator[](size_t pos)
  7.                 {
  8.                         assert(pos < _size);
  9.                         return _str[pos];
  10.                 }
  11.                 const char& operator[](size_t pos) const//给const对象使用
  12.                 {
  13.                         assert(pos < _size);
  14.                         return _str[pos];
  15.                 }
  16.         };
  17. //...
  18. }
复制代码
2.4.2迭代器

  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 typedef char* iterator;//迭代器可以实现成指针,也可以不
  7.                 //可以使用原生指针来代替iterator
  8.                 iterator begin()
  9.                 {
  10.                         return _str;
  11.                 }
  12.                 iterator end()
  13.                 {
  14.                         return _str+_size;
  15.                 }
  16.         };
  17. //...
  18. }
复制代码
2.4.3范围for

  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         //...
  6.         };
  7.         void test_string2()
  8.         {
  9. //1. []
  10.                 string s1("hello world");
  11.                 for (size_t i = 0; i <s1.size() ; i++)
  12.                 {
  13.                         cout << s1[i] << " ";
  14.                 }
  15.                 cout << endl;
  16. //2.迭代器
  17.                 string::iterator it=s1.begin();//在里面typedef的或者是内部类
  18.                 while (it != s1.end())
  19.                 {
  20.                         cout << *it << " ";
  21.                         it++;
  22.                 }
  23.                 cout << endl;
  24. //3.范围for
  25.                 //这里还支持范围for
  26.                 for (auto ch : s1)//底层代码和上面的迭代器类似
  27.                 {
  28.                         //auto ch = *it;
  29.                         cout << ch << " ";
  30.                 }
  31.                 cout << endl;
  32.                 //范围for的本质是替换成迭代器,编译时直接替换过去
  33.                 //而且有很严格的规范,名字变化一下都是不可以的
  34.         }
  35. }
复制代码


2.5复用reserve实现尾插

   实现尾插的三种方式:
  1.push_back
  2.append
  3.+=
  
  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 void reserve(size_t n)
  7.                 {
  8.                         assert(n > _capacity);
  9.                         char* str1 = new char[n+1];//失败会抛异常
  10.                         strcpy(str1,_str);//会把'\0'拷贝过去
  11.                         delete[] _str;//越界会在这里崩溃
  12.                         _str = str1;
  13.                         _capacity = n;
  14.                 }
  15.                 void push_back(char ch)
  16.                 {//扩容问题
  17.                         if (_size == _capacity)
  18.                         {
  19.                                 reserve(_capacity==0 ? 4 :_capacity * 2);
  20.                         }
  21.                         _str[_size] = ch;
  22.                         _size++;
  23.                         _str[_size] = '\0';
  24.                 }
  25.                 void append(const char* str)
  26.                 {
  27.                         size_t len = strlen(str);
  28.                         if (_size + len > _capacity)
  29.                         {
  30.                                 reserve(_size + len);
  31.                         }
  32.                         strcpy(_str + _size, str);
  33.                         _size += len;
  34.                 }
  35.                 string& operator+=(char ch)
  36.                 {
  37.                         push_back(ch);
  38.                         return *this;
  39.                 }
  40.                 string& operator+=(const char* str)
  41.                 {
  42.                         append(str);
  43.                         return *this;
  44.                 }
  45. //...
  46.         };
  47.         void test_string3()
  48.         {
  49.                 string s1("hello world");
  50.                 cout << s1.c_str() << endl;
  51.                 s1.push_back('0');
  52.                 cout << s1.c_str() << endl;
  53.                 s1.append("hello lxy");
  54.                 cout << s1.c_str() << endl;
  55.                 s1 += '$';
  56.                 cout << s1.c_str() << endl;
  57.                 s1+="hellox";
  58.                 cout << s1.c_str() << endl;
  59.         }
  60. }
复制代码
2.6 insert和erase

insert错误写法:
  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 void insert(size_t pos,char ch)
  7.                 {
  8.                         assert(pos <= _size);
  9.                         if (_size == _capacity)
  10.                         {
  11.                                 reserve(_capacity == 0 ? 4 : _capacity * 2);
  12.                         }
  13.                         size_t end = _size;//头插会出问题,这里是无符号数
  14.                         //int end = _size; //改为有符号数也是行不通的
  15.                         //在操作符,如 >= 两边,类型不同时,会发生类型提升
  16.                         //这里end虽然是有符号数,但会被提升成无符号数
  17.                         while (end >= pos)
  18.                         {
  19.                                 _str[end + 1] = _str[end];
  20.                                 end--;
  21.                         }
  22.                         _str[pos] = ch;
  23.                         _size++;
  24.                 }
  25. //...
  26.         };
  27.         void test_string4()
  28.         {
  29.                 string s1("hello world");
  30.                 cout << s1.c_str() << endl;
  31.                 s1.insert(5, '#');
  32.                 cout << s1.c_str() << endl;
  33.                 s1.insert(s1.size(), '#');
  34.                 cout << s1.c_str() << endl;
  35.                 s1.insert(0, '#');//头插
  36.                 cout << s1.c_str() << endl;
  37.         }
  38. }
复制代码
正确写法1:
  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 void insert(size_t pos,char ch)
  7.                 {
  8.                         assert(pos <= _size);
  9.                         if (_size == _capacity)
  10.                         {
  11.                                 reserve(_capacity == 0 ? 4 : _capacity * 2);
  12.                         }
  13.                         size_t end = _size+1;  
  14.                         while (end>pos)
  15.                         {
  16.                                 _str[end] = _str[end-1];
  17.                                 end--;
  18.                         }
  19.                         _str[pos] = ch;
  20.                         _size++;
  21.                 }
  22. //...
  23.         };
  24. }
复制代码
正确写法2:
  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 void insert(size_t pos,char ch)
  7.                 {
  8.                         assert(pos <= _size);
  9.                         if (_size == _capacity)
  10.                         {
  11.                                 reserve(_capacity == 0 ? 4 : _capacity * 2);
  12.                         }
  13.                         int end = _size;
  14.                         while (end>=(int)pos)//或者把这里强转一下,避免出现类型提升
  15.                         {
  16.                                 _str[end] = _str[end-1];
  17.                                 end--;
  18.                         }
  19.                         _str[pos] = ch;
  20.                         _size++;
  21.                 }
  22. //...
  23.         };
  24. }
复制代码

  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 void insert(size_t pos,size_t len=npos)
  7.                 {
  8.                 }
  9.                 void insert(size_t pos,const char* str)
  10.                 {
  11.                 }
  12.                 void erase(size_t pos,size_t len=npos)
  13.                 {
  14.                 }
  15.         private:
  16.                 char* _str;
  17.                 size_t _size;
  18.                 size_t _capacity;
  19. //注意
  20.                 //const static size_t npos =-1;  //特例
  21.                 //这里是特殊情况,按理说不能在这里初始化
  22.                 //这里给值给的是缺省值,缺省值是给初始化列表使用的
  23.                 //静态成员变量不会去执行初始化列表,它不属于对象,它属于整个类,按理说要在类外面初始化
  24.                 //但是const修饰的静态的整型可以,所以这里既是定义,又是定义初始化
  25.                
  26.                 //const static double npos = 1.2;//这样都不会去支持
  27.                 const static size_t npos;
  28.         };
  29.         const size_t string::npos = -1;
  30. }
复制代码
2.7运算符重载

  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.         public:
  6. //运算符重载
  7. //设计成非成员函数更好,便于模板的使用
  8.                 bool operator<(const string& str1)
  9.                 {
  10.                         return strcmp(_str, str1._str)<0;
  11.                 }
  12.                 bool operator==(const string& str1)
  13.                 {
  14.                         return strcmp(_str, str1._str)==0;
  15.                 }
  16.                 bool operator<=(const string& str1)
  17.                 {
  18.                         return *this<str1 || *this==str1;
  19.                 }
  20.                 bool operator>=(const string& str1)
  21.                 {
  22.                         return !(*this<str1);
  23.                 }
  24.                 bool operator>(const string& str1)
  25.                 {
  26.                         return !(*this<=str1);
  27.                 }
  28.                 bool operator!=(const string& str1)
  29.                 {
  30.                         return !(*this == str1);
  31.                 }
  32. //...
  33.         };
  34. }
复制代码
2.8流插入

  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.     public:
  6.                 typedef const char* const_iterator;//const迭代器指向的内容不能修改
  7.                 //指针本身可以修改,指向的内容不能修改
  8.                 const_iterator begin() const
  9.                 {
  10.                         return _str;
  11.                 }
  12.                 const_iterator end() const
  13.                 {
  14.                         return _str + _size;
  15.                 }
  16.     //...
  17.         };
  18.         //没有必要定义为友元,想访问私有成员变量才需要定义为友元
  19.         ostream& operator<<(ostream& out, const string& s)
  20.         {
  21.                 1.
  22.                 //for (size_t i = 0; i < s.size(); i++)
  23.                 //{
  24.                 //        out << s[i];
  25.                 //}
  26.                 //return out;
  27.                 //2.可以使用范围for,但要替换成const迭代器
  28.                 for (auto ch : s)
  29.                         out << ch;
  30.                 return out;
  31.         }
  32. }
复制代码
2.9流提取和clear

  1. namespace jxy
  2. {
  3.         class string
  4.         {
  5.     public:
  6.                 void clear()
  7.                 {
  8.                         _str[0] = '\0';
  9.                         _size = 0;
  10.                 }
  11.     //...
  12.         };
  13.         //没有必要定义为友元,想访问私有成员变量才需要定义为友元
  14.         istream& operator>>(istream& in,string& s)
  15.         {//流提取要从缓冲区中取一个个的字符
  16.                 s.clear();
  17.                 char ch;
  18.                 //in >> ch; //这样写获取不到空格
  19.                 ch = in.get();
  20.                 while (ch != ' ' && ch != '\n')
  21.                 {
  22.                         s += ch;
  23.                         //in >> ch;
  24.                         ch = in.get();
  25.                 }
  26.                 return in;
  27.         }
  28.         void test_string6()
  29.         {
  30.                 string s1("hello world");
  31.                 cout << s1 << endl;
  32.                 cin >> s1;
  33.                 cout << s1 << endl;
  34.         }
  35. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

雁过留声

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表