代码分析c++中string类

作者:瞭望天空 时间:2023-04-24 17:15:29 

一:回顾

(1)c++中的string类是在面试中和笔试中经常考的题目; 工程代码免费下载 string类的自行实现

(2)c++中的string类和fstream类合起来是处理外部数据的利器;

(3)string类经常用到find find_first_of find_first_not_of find_last_of find_last_not_of substr replace等,以及联合使用来达到java中的split和trim

(4) 使用friend 仅仅是在类中进行声明的非内部 却可以访问内部成员的外部函数,而且在外部不再需要friend关键字;它与成员函数的区别是,friend和外部函数不含有this对象指针;本文用到了const 定义的全局最大值最小值变量(代替#define)

(5) 有些函数返回的是MyString& 、Char& 等(引用),MyString、Char 等(传值)这得看你返回的对象是函数的局部变量还是全局变量(或者类当前对象成员变量);前者只能返回一个MyString、Char 等;后者强烈建议返回MyString& 、Char& 等(引用);

(6)有些函数的参数是const MyString& ,有些是MyString& (引用);这是为什么?前者是把外部值传提到子函数内部,且不允许改变;后者是作为函数的返回值传递进去的,返回的结果为函数的处理结果(而不用函数自身返回值了)。

二:下面是简单的实现了一下string类,参照的是STL源码,但是自己理解的还是不够深,难免有一些错误,请各位指教

(1)MyString.h文件


#ifndef MYSTRING_H
#define MYSTRING_H
#include "MyExcept.h"
#include <cstring>
#include <iostream>
const int INI_MAX = 0x7fffffff;//2^32npos
const int INI_MIN = 0x80000000;// -2^32
const int npos = 0xffffffff;// npos
using namespace std;

class MyString
{
 public:
 // constructor
 MyString();//
 MyString(const MyString &);//
 MyString(const char *);
 MyString(const size_t,const char);
 // destructor
 ~MyString();
 // attributes

size_t length();// 字符串长度
 bool isEmpty();// 返回字符串是否为空
 const char* c_str();// 返回c风格的trr的指针
 // friend funs
 // read writer operations
 friend ostream& operator<< (ostream&, const MyString&);
 friend istream& operator>> (istream&, MyString&);
 //add operation
 friend MyString operator+(const MyString&,const MyString&);
 // compare operations
 friend bool operator==(const MyString&,const MyString&);
 friend bool operator!=(const MyString&,const MyString&);
 friend bool operator<(const MyString&,const MyString&);
 friend bool operator<=(const MyString&,const MyString&);
 friend bool operator>(const MyString&,const MyString&);
 friend bool operator>=(const MyString&,const MyString&);
 // 成员函数实现运算符重载,其实一般需要返回自身对象的,成员函数运算符重载会好一些
 // index operation
 char& operator[](const size_t);
 const char& operator[](const size_t)const;
 // =
 MyString& operator=(const MyString&);
 // +=
 MyString& operator+=(const MyString&);
 // +=
 //MyString operator+=(const MyString&); cannot be overloaded
 // 成员操作函数
 // substr
 MyString substr(size_t pos,const size_t n);
 // append
 MyString& append(const MyString&);
 //insert
 MyString& insert(size_t,const MyString&);
 //assign 替换
 MyString& assign(MyString&,size_t,size_t);
 // erase 删除
 MyString& erase(size_t,size_t);
 //find_first_of 查找某一个字符 size_t 是非符号数的,重载
 // 查找在字符串中第一个与str中的某个字符匹配的字符,返回它的位置。
 //搜索从index开始,如果没找到就返回string::npos
 int find_first_of(const char* str,size_t index=0);
 int find_first_of(const char ch,size_t index=0);
 int find_first_of(const MyString &,size_t index=0);
 // 在字符串中查找第一个与str中的字符都不匹配的字符,返回它的位置。搜索从index开始。如果没找到就返回string::nops
 int find_first_not_of(const char* str,size_t index=0);
 int find_first_not_of(const char ch,size_t index=0);
 int find_first_not_of(const MyString&,size_t index=0);
 // swap
 void swap(MyString& lhs,MyString& rhs);
 // replace_all
 MyString& replace_all(const char oldc,const char newc=NULL);
 MyString& replace(size_t index,size_t num1,size_t num2,const char ch);
 //find
 int find(const char* str,size_t index=0);
 int find(const MyString& str,size_t index=0);
 int find(const char ch,size_t index=0);

//private
 private:
 char *p_str;
 size_t strLength;
};
#endif // MYSTRING_H

(2)MyString.cpp文件


#include "MyString.h"
#include <cassert>

// constructor
 MyString::MyString():p_str(NULL),strLength(0){}

MyString::MyString(const MyString &str)//
 {
   if(NULL == str.p_str)
   {
     return;
   }
   strLength = str.strLength;
   p_str = new char[strLength+1];
   strcpy(p_str,str.p_str);
 }
 MyString::MyString(const char *str)
 {
   if(NULL == str)
   {
     return;
   }
   strLength = strlen(str);
   p_str = new char[strLength+1];
   strcpy(p_str,str);
 }
 MyString::MyString(const size_t len,const char ch)
 {
   if(NULL == ch)
   {
     return;
   }
   strLength = len;
   p_str = new char[strLength+1];
   for(size_t i=0;i<strLength;i++)
   {
     p_str[i] = ch;
   }
   p_str[strLength] = '\0';// 因为strset以'\0'结束的
   cout << p_str << " &&" << endl;
   //strset(p_str,ch);
   //cout << p_str[0] << ",,,"<<strlen(p_str) << "," << strLength << endl;
 }
 // destructor
 MyString::~MyString()
 {
   delete[] p_str;
 }

// attributes
 size_t MyString::length()// 字符串长度
 {
   return strLength;
 }
 bool MyString::isEmpty()// 返回字符串是否为空
 {
   return strLength==0?true:false;
 }
 const char* MyString::c_str()
 {
   return p_str;
 }
 // 为什么不是引用呢??? friend 使用在类里面进行声明的,外面就不需要了,而且友元函数不属于类的成员函数,所以不用MyString::
 // ostream
 ostream& operator<< (ostream& out,const MyString &str)
 {
   if(str.p_str != NULL)
   {
     out << str.p_str;
   }
   return out;
 }
 // istream,一个是const另一个不是,根据变还是不变
 istream& operator>> (istream& in, MyString& str)
 {
   char tmp[100];// 临时字符串
   if(in>>tmp)
   {
     delete[] str.p_str;
     str.strLength = strlen(tmp);
     str.p_str = new char[str.strLength+1];
     strcpy(str.p_str,tmp);
   }
   return in;
 }
 // + 加
 MyString operator+(const MyString& lhs,const MyString& rhs)
 {
   MyString ret;
   ret.strLength = lhs.strLength + rhs.strLength;
   ret.p_str = new char[ret.strLength+1];
   strcpy(ret.p_str,lhs.p_str);
   strcat(ret.p_str,rhs.p_str);
   return ret;
 }
 // compare operations
 bool operator==(const MyString& lhs,const MyString& rhs)
 {
   return strcmp(lhs.p_str,rhs.p_str)==0?true:false;
 }
 bool operator!=(const MyString& lhs,const MyString& rhs)
 {
   return strcmp(lhs.p_str,rhs.p_str)!=0?true:false;
 }
 bool operator<(const MyString& lhs,const MyString& rhs)
 {
   return strcmp(lhs.p_str,rhs.p_str)<0?true:false;
 }
 bool operator<=(const MyString& lhs,const MyString& rhs)
 {
   return strcmp(lhs.p_str,rhs.p_str)<=0?true:false;
 }
 bool operator>(const MyString& lhs,const MyString& rhs)
 {
   return strcmp(lhs.p_str,rhs.p_str)>0?true:false;
 }
 bool operator>=(const MyString& lhs,const MyString& rhs)
 {
   return strcmp(lhs.p_str,rhs.p_str)>=0?true:false;
 }
 // 成员函数实现运算符重载
 // index operation
 char& MyString::operator[](const size_t index)
 {
   if(index<0 || index>=strLength)
   {
     throw Outofbond() ;
   }
   return p_str[index];
 }
 const char& MyString::operator[](const size_t index)const
 {
   if(index<0 || index>=strLength)
   {
     throw Outofbond();
   }
   return p_str[index];
 }
  // = 赋值构造函数(判断是否是自身) 为什么要这样删除呢?
 MyString& MyString::operator=(const MyString& other)
 {
   if(this != &other)
   {
     if(strLength<other.strLength)
     {
       delete[] p_str;
       p_str = new char[other.strLength+1];
     }
     strLength = other.strLength;
     strcpy(p_str,other.p_str);
   }// 这样可能会产生多余的未释放的空间
   return *this;
 }
 // += 相当于返回的是备份的,内部对象的销毁,不影响的 和 下面的完全不一样的
//  MyString MyString::operator+=(const MyString& other)
//  {
//    if(NULL == other.p_str)
//    {
//      return *this;
//    }
//    MyString ret;
//    ret.strLength = strLength + other.strLength;
//    ret.p_str = new char[ret.strLength+1];
//    strcpy(ret.p_str,p_str);
//    strcat(ret.p_str,other.p_str);
//    return ret;
//  }
 // 返回的是当前对象的引用,当前对象就在调用函数里,所以不会销毁的
 // 判断一下是否是自身相加
 MyString& MyString::operator+=(const MyString& other)
 {
   if(NULL == other.p_str)
   {
     return *this;
   }
   if(this == &other)
   {
     MyString copy(*this);
     return *this += copy;
   }// 必须判断是否相等的,而且要+=的,这样相当于调用了自身,但是这次直接下面去了,不进入if的
   strLength += other.strLength;
   //strLength *= 2;
   char *p_old = p_str;
   p_str = new char[strLength+1];
   strcpy(p_str,p_old);
   strcat(p_str,other.p_str);
   delete[] p_old;// 删除旧的空间
   return *this;
 }
 // 成员操作函数
 // substr 返回应用是不行的,错误的;取从pos开始的n个字符组成的子串
 //MyString& MyString::substr(size_t pos,const size_t n)
 MyString MyString::substr(size_t pos,const size_t n)
 {
   if((pos+n)>=strLength)
   {
     throw Outofbond();
   }
   MyString ret;
   ret.strLength = n;
   //ret.p_str = new char[n+1];
   ret.p_str = new char[ret.strLength+1]; //也可以
   for(size_t i=0;i<n;i++)
   {
     ret.p_str[i] = p_str[pos+i];
   }
   ret.p_str[n] = '\0';
//    for(size_t i=0;i<ret.strLength;i++)
//    {
//      ret[i] = (*this)[pos+i];
//      cout << ret[i] << ",,";
//    }// 也行的,利用刚刚重载的【】,这样更好,不用再次判断越界了,不知道为什么,报错误的
//    ret[ret.strLength] = '\0';
   return ret;
 }
 // append 同 += 追加到末尾
 MyString& MyString::append(const MyString& other)
 {
   *this += other;// 利用刚刚那重载的+=
   return *this;
 }
 //insert 从pos开始的插入other
 MyString& MyString::insert(size_t pos,const MyString& other)
 {
   if(pos<0 || pos>=strLength)
   {
     throw Outofbond();
   }
   char *p_old = p_str;
   strLength += other.strLength;
   p_str = new char[strLength+1];
   for(size_t i=0;i<pos;i++)
   {
     *(p_str+i) = *(p_old+i);
   }
   for(size_t i=pos;i<other.strLength+pos;i++)
   {
     *(p_str+i) = other.p_str[i-pos];
   }
   for(size_t i=other.strLength+pos;i<strLength;i++)
   {
     *(p_str+i) = p_old[i-other.strLength];
   }
   *(p_str+strLength) = '\0';
   return *this;
 }
 //assign 替换 用other的POS开始的n对应替换this的pos开始的
 MyString& MyString::assign(MyString&other,size_t pos,size_t n)
 {
//    if(pos<0 || pos>=strLength)
//    {
//      throw Outofbond();
//    }
   assert(pos>0 && pos<strLength);// assert 的好处
   assert(pos+n<other.strLength);
   if(strLength < pos + n)
   {
     char *p_old = p_str;
     strLength = pos + n;
     p_str = new char[strLength+1];
     for(size_t i=0;i<pos;i++)
     {
       *(p_str+i) = *(p_old+i);
     }
     delete[] p_old;
   }
   for(size_t i=pos;i<pos+n;i++)
   {
     *(p_str+i) = other.p_str[i];
   }
   *(p_str+pos+n) = '\0';
   return *this;
 }
 // erase 删除 这个方法并不是很好的,并没有释放所erase的空间,请看下面的
//  MyString& MyString::erase(size_t pos,size_t n)
//  {
//    if((pos+n)>strLength)
//    {
//      throw Outofbond();
//    }
//    size_t index = pos + n;
//    while(*(p_str+index)!='\0')
//    {
//      *(p_str+index-n) = *(p_str+index);
//      ++index;
//    }
//    *(p_str+index-n) = '\0';
//    return *this;
//  }
 // erase 删除 从pos开始的n个字符
 MyString& MyString::erase(size_t pos,size_t n)
 {
   if((pos+n)>strLength)
   {
     throw Outofbond();
   }
   char *p_old = p_str;
   strLength -= n;
   p_str = new char[strLength+1];
   for(size_t i=0;i<pos;i++)
   {
     p_str[i] = p_old[i];
   }
   for(size_t i=pos;i<strLength;i++)
   {
     p_str[i] = p_old[i+n];
   }
   *(p_str+strLength) = '\0';
   return *this;
 }
 //find_first_of 查找某一个字符 size_t 是非符号数的
 // 查找在字符串中第一个与str中的某个字符匹配的字符,返回它的位置。
 //搜索从index开始,如果没找到就返回string::npos
 int MyString::find_first_of(const char* str,size_t index)
 {
   if(NULL == str || index >=strLength)
     return npos;
   int tmp_len = strlen(str),j;
   size_t flag,min_index = INI_MAX;
   for(j=0;j<tmp_len;j++)
   {
     flag = npos;
     for(size_t i=index;i<strLength;i++)
     {
       if(str[j] == p_str[i])
       {
         flag = i;
         break;
       }
     }
//      indexs[j] = flag;
     if(flag != npos)
     {
       min_index = min_index<flag?min_index:flag;
     }
   }
//    for(j=0;j<tmp_len;j++)
//    {
//      if(indexs[j]!=npos)
//        min = min<indexs[j]?min:indexs[j];
//    }
   if(min_index == INI_MAX)
   {
     return npos;
//      min_index = npos;
//      cout << "---npos----" << min_index << ",,,,";
   }
   return min_index;
 }
 int MyString::find_first_of(const char ch,size_t index)
 {
   if(NULL == ch || index >=strLength)
     return npos;
   int j;
   size_t flag = npos;
   for(size_t i=index;i<strLength;i++)
   {
     if(ch == p_str[i])
     {
       flag = i;
       break;
     }
   }
   return flag;
 }
 int MyString::find_first_of(const MyString& str,size_t index)
 {
   if(NULL == str || index >=strLength)
     return npos;
   int j;
   size_t flag,min_index = INI_MAX;
   for(j=0;j<str.strLength;j++)
   {
     flag = npos;
     for(size_t i=index;i<strLength;i++)
     {
       if(str[j] == p_str[i])
       {
         flag = i;
         break;
       }
     }
     if(flag != npos)
     {
       min_index = min_index<flag?min_index:flag;
     }
   }
   if(min_index == INI_MAX)
   {
     return npos;
   }
   return min_index;
 }
 // 在字符串中查找第一个与str中的字符都不匹配的字符,返回它的位置。
 //搜索从index开始。如果没找到就返回string::nops O(N^2)
 int MyString::find_first_not_of(const char *str,size_t index)
 {
   if(NULL == str || index >=strLength)
     return npos;
   size_t i=0,j=0;
   size_t tmp_len = strlen(str);
   for(i=index;i<strLength;i++)
   {
     for(;j<tmp_len;j++)
     {
       if(p_str[i]==str[j])
         break;
     }
     if(j==tmp_len)
       break;// 根据跳出的内层for的条件判断,找到即结束循环
   }
   if(i==strLength)
     return npos;// 未找到,// 根据跳出的内层for的条件判断,找到即结束循环
   return i;
 }
 int MyString::find_first_not_of(const MyString& str,size_t index)
 {
   if(NULL == str || index >=strLength)
     return npos;
   size_t i=0,j=0;
   for(i=index;i<strLength;i++)
   {
     for(;j<str.strLength;j++)
     {
       if(p_str[i]==str[j])
         break;// 如果相等 本轮i就无效了,进行下一轮
     }
     if(j==str.strLength)
       break;// 根据跳出的内层for的条件判断,找到即结束循环
   }
   if(i==strLength)
     return npos;// 未找到,// 根据跳出的内层for的条件判断,找到即结束循环
   return i;
 }
 int MyString::find_first_not_of(const char ch,size_t index)
 {
   if(NULL == ch || index >=strLength)
     return npos;
   size_t i=0;
   for(i=index;i<strLength;i++)
   {
     if(p_str[i]!=ch)// 跟上面的略微不同,找一个不等就可以了
       break;
   }
   if(i==strLength)
     return npos;// 未找到,// 根据跳出的内层for的条件判断,找到即结束循环
   return i;
 }
 // swap 都得变得,所以非const
 void MyString::swap(MyString& lhs,MyString& rhs)
 {
   lhs.strLength ^= rhs.strLength;
   rhs.strLength ^= lhs.strLength;
   lhs.strLength ^= rhs.strLength;
   char *p_tmp = rhs.p_str;
   rhs.p_str = lhs.p_str;
   lhs.p_str = p_tmp;
 }
 // replace_all 这个东西还是不太好弄的啊,不是很理想
 MyString& MyString::replace_all(const char oldc,const char newc)
 {
   if(NULL == oldc)
   {
     return *(this);
   }
   for(size_t i=0;i<strLength;i++)
   {
     if(p_str[i] == oldc)
     {
       p_str[i] = newc;
     }
   }
   return *(this);
 }
 MyString& MyString::replace(size_t index,size_t num1,size_t num2,const char ch)
 {

}
 // find 函数
 int MyString::find(const char* str,size_t index)
 {
   assert(str!=NULL&&index<strLength);
   // kmp 中的getnext函数
   size_t len = strlen(str);
   size_t next[len+1];
   size_t j,k;
   next[0] = npos;
   j = 0;
   k = npos;
   while(j<len)
   {
     if(k==npos || str[j]==str[k])
     {
       j++;
       k++;
       next[j] = k;
     }
     else
       k = next[k];
   }
   // kmp 算法
   k = index;
   j = 0;
   while(p_str[k]!='\0')
   {
     if(j==0 || p_str[k]==str[j])
     {
       k++;
       j++;
     }
     else
     {
       j = next[j];// 消除指针回溯
     }
     if(str[j] == '\0')//匹配成功
       return k-j;
   }
   return npos;
 }
 int MyString::find(const MyString& str,size_t index)
 {
//    if(this == &str)
//    {
//      MyString other(*this);
//      find(other,index);
//    }
   assert(NULL!=str && index<strLength);
   // kmp 中的getnext函数

size_t next[str.strLength+2];
   size_t j,k;
   next[0] = npos;
   j = 0;
   k = npos;
   while(j<str.strLength)
   {
     if(k==npos || str.p_str[j]==str.p_str[k])
     {
       j++;
       k++;
       next[j] = k;
     }
     else
       k = next[k];
   }
   int i;
   for(i=1;i<=j;i++)
     cout << next[i] << ",";
   // kmp 算法
   k = index;
   j = 0;
   while(p_str[k]!='\0')
   {
     if(j==0 || p_str[k]==str.p_str[j])
     {
       k++;
       j++;
     }
     else
     {
       j = next[j];// 消除指针回溯
     }
     if(str.p_str[j] == '\0')//匹配成功,不知道为什么调用自身的str[]重载总是报错的
       return k-j;
   }
   if(str.p_str[j] == '\0')// 同一个字符串
     return k-j;
   return npos;
 }
 int MyString::find(const char ch,size_t index)
 {
   assert(NULL!=ch && index<strLength);
   for(size_t i=index;i<strLength;i++)
   {
     if(p_str[i] == ch)
       return i;
   }
   return npos;
 }

(3)测试函数main.cpp


#include "MyString.h"
#include <iostream>
using namespace std;

int main()
{
 int n;
 int choose = 1;
 int p,l;
 char cs[100];
 MyString s1;
 MyString s2("hello");
 MyString s3 = "HELLO";
 cout << "***** welcome *****\n";
 cout << "******* MADE BY zyp **********\n";
 cout << "s1= " << s1 << "s2= " << s2 << "s3= " << s3 << endl;
 cout << "请输入一个长度小于100的字符串:例如world\n";
 cin >> s1;
 s1 = s1;
 //s1 = s1+s1;
 s1 += s1;
 MyString s4(s1);
 s4.append(s1);
 s2.insert(2,s3);
 s1.erase(4,4);
 s1.assign(s2,1,7);
 cout << "s1= " << s1 << "s2= " << s2 << "s3= " << s3 << "s4= " << s4 << endl;
 s2 = s4.substr(2,7);
 cout << "s4[3]= " << s4[3] << s4.length() << (s1>=s2) << "s4.substr() " << s2 << endl;
 cout << "s1.find_first_of(beLE,2):" << s1.find_first_of("beLE",2) << ",s1.find_first_of(a,3):" << s1.find_first_of('a',3) << ",s1.find_first_of(s3,2):" << s1.find_first_of(s3,2) << endl;
 MyString s5(5,'b');
 s5 += s5;
 //s5.append(s5);// 不知道为什就是不能append
 cout << "s5 = " << s5 << "s5.find_first_not_of(aeHLEOl,2):" << s5.find_first_not_of("aeHLEOl",2) << "s5.find_first_not_of(aeHLEOl,0):" << s5.find_first_not_of("aeHLEOl") << endl;
 cout << "s5.find_first_not_of(s1,2):" << s5.find_first_not_of(s1,2) << "s5.find_first_not_of(b,2):" << s5.find_first_not_of('b',2) << endl;
 swap(s1,s5);
 s5.replace_all('a','J');
 MyString s6("LLO");
 cout << s1 << "," << s5 << "s5.find(LLO,0) " << s5.find("LLO",0) << "s5.find(s6,0) " << s5.find(s5) << endl;
 cout << npos << endl;
 return 0;
}

三:感悟

(1)耗时将近2天的实现了它,自己与其从中学到了很多,倒不如说是重新认识了string类;

(2)自己知道这个简单的string类,距离string源代码还差的很远很远;但是它帮助我更好的理解了string类,至少会简单的应用了。

(3)简单的实现了一下string类,参照的是STL源码,但是自己理解的还是不够深,难免有一些错误,请各位指教,万分感谢!

(4)下一步进军list

来源:http://blog.csdn.net/u010700335/article/details/40979037

标签:c++,string类
0
投稿

猜你喜欢

  • Java 中Timer和TimerTask 定时器和定时任务使用的例子

    2022-04-21 17:50:27
  • IDEA类与方法注释模板设置图文教程(非常详细)

    2022-02-24 10:18:29
  • C#调用dll报错:无法加载dll,找不到指定模块的解决

    2023-08-23 23:36:12
  • 解决CollectionUtils.isNotEmpty()不存在的问题

    2021-11-15 12:03:17
  • 解决Java中properties文件编码问题

    2022-07-21 14:09:06
  • springboot如何重定向外部网页

    2022-11-12 05:19:19
  • 配置SpringBoot方便的切换jar和war的方法示例

    2023-11-22 11:57:54
  • android实现点击图片全屏展示效果

    2023-12-06 22:42:25
  • c#深拷贝文件夹示例

    2023-07-24 07:50:40
  • Android P实现静默安装的方法示例(官方Demo)

    2022-04-05 20:06:13
  • Android开发之菜单(menu)用法实例分析

    2022-06-22 23:13:28
  • 详解基于java的Socket聊天程序——客户端(附demo)

    2021-06-28 04:19:54
  • 详解maven中profiles使用实现

    2022-11-13 23:14:24
  • C#使用StopWatch获取程序毫秒级执行时间的方法

    2023-12-21 02:44:06
  • JAVA编程不能不知道的反射用法总结

    2023-08-25 18:41:06
  • java判断http地址是否连通(示例代码)

    2023-08-05 03:24:05
  • Java语言中的文件数据流示例详解

    2023-05-12 19:55:29
  • 基于spring cloud多个消费端重复定义feign client的问题

    2022-09-18 11:34:23
  • java实现简单的俄罗斯方块

    2021-08-02 18:13:11
  • Java 字符串反转实现代码

    2023-01-09 10:04:52
  • asp之家 软件编程 m.aspxhome.com