Code analysis of c++ string class

  • 2020-06-03 07:47:08
  • OfStack

1: review

(1) The string in c++ is often tested in interviews and written tests; Free download of engineering code string class implementation

(2) string class and fstream class in c++ are sharp tools for processing external data.

(3) string class often USES find find_first_of ES18en_ES19en_ES20en_ES24en find_last_of find_ES26en_ES28en substr replace, etc., as well as combined use to reach split and trim in java

(4) The use of friend is only a non-internal declaration in the class but can access the external functions of internal members, and the friend keyword is no longer needed externally; It differs from member functions in that friend and external functions do not contain Pointers to this objects; This article USES the global Max min variable defined by const (instead of #define)

(5) Some functions return MyString & , Char & MyString, Char, etc. This depends on whether the object you return is a local or global variable of the function (or a member of the class's current object); The former can only return 1 MyString, Char, etc. The latter strongly recommends a return to MyString & , Char & Etc. (quote);

(6) Some functions take arguments of const MyString & Some are MyString & (quote); Why is that? The former is to pass the external value inside the subfunction, and is not allowed to change; The latter is passed in as the return value of the function, which is the processing result of the function (rather than the return value of the function itself).

2: The following is a simple implementation of the string class, reference is STL source code, but their understanding is not deep enough, there are inevitably 1 error, please advise

(1) MyString. h file


#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();//  String length 
  bool isEmpty();//  Returns whether the string is empty 
  const char* c_str();//  return c In the style of trr A pointer to the 
  // 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&);
  //  Member functions implement operator overloading , Actually, 1 Member function operator overloading is good if you need to return an object of your own 1 some 
  // 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
  //  Member manipulation function 
  // substr
  MyString substr(size_t pos,const size_t n);
  // append
  MyString& append(const MyString&);
  //insert
  MyString& insert(size_t,const MyString&);
  //assign  replace 
  MyString& assign(MyString&,size_t,size_t);
  // erase  delete 
  MyString& erase(size_t,size_t);
  //find_first_of  To find a 1 A character  size_t  Nonsign number , overloading 
  //  Finds the first in a string 1 with str Returns the position of a matched character in. 
  // Search from index Go ahead and return if you don't find it 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);
  //  Look for the value in the string 1 with str Returns the position of a character if none of the characters in. Search from index Start. Return if not found 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';//  because strset In order to '\0' The end of the 
    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()//  String length 
  {
    return strLength;
  }
  bool MyString::isEmpty()//  Returns whether the string is empty 
  {
    return strLength==0?true:false;
  }
  const char* MyString::c_str()
  {
    return p_str;
  }
  //  Why not quote??  friend  Use in the class declaration, the outside is not needed, and the friend function is not a member of the class function, so not MyString : : 
  // ostream
  ostream& operator<< (ostream& out,const MyString &str)
  {
    if(str.p_str != NULL)
    {
      out << str.p_str;
    }
    return out;
  }
  // istream,1 One is const On the other 1 No, according to the change or the same 
  istream& operator>> (istream& in, MyString& str)
  {
    char tmp[100];//  Temporary string 
    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;
  }
  // +  add 
  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;
  }
  //  Member functions implement operator overloading 
  // 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];
  }
   // =  Assignment constructor (determines if it is itself)   Why did you delete it this way? 
  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);
    }//  This may create excess unreleased space 
    return *this;
  }
  // +=  Equivalent to the return of a backup, internal object destruction, does not affect   and   Not at all 1 The sample of 
//  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;
//  }
  //  Returns a reference to the current object, which is in the calling function and will not be destroyed 
  //  judge 1 Is it the sum of itself 
  MyString& MyString::operator+=(const MyString& other)
  {
    if(NULL == other.p_str)
    {
      return *this;
    }
    if(this == &other)
    {
      MyString copy(*this);
      return *this += copy;
    }//  You have to decide whether it's equal, and you have to += Of, that's the same thing as calling itself, but this time I'm going to go straight down and not go in if the 
    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;//  Delete the old space 
    return *this;
  }
  //  Member manipulation function 
  // substr  Return application is not ok, wrong; Pick up from pos The start of the n A substring of two characters 
  //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]; // Can also be 
    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] << ",,";
//    }//  Ok, use the [] that I just reloaded , It's better that way, you don't have to cross the line again , I don't know why, the wrong one 
//    ret[ret.strLength] = '\0';
    return ret;
  }
  // append  with  +=  Append to 
  MyString& MyString::append(const MyString& other)
  {
    *this += other;//  Take advantage of that heavy load +=
    return *this;
  }
  //insert  from pos Initial insertion 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  replace   with other the POS The start of the n Corresponding to replace this the pos The start of the 
  MyString& MyString::assign(MyString&other,size_t pos,size_t n)
  {
//    if(pos<0 || pos>=strLength)
//    {
//      throw Outofbond();
//    }
    assert(pos>0 && pos<strLength);// assert  The benefits of 
    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  delete   This method is not very good, there is no release erase The space of , Look at the following 
//  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  delete   from pos The start of the n A character 
  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  To find a 1 A character  size_t  Nonsign number 
  //  Finds the first in a string 1 with str Returns the position of a matched character in. 
  // Search from index Go ahead and return if you don't find it 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;
  }
  //  Look for the value in the string 1 with str Returns the position of a character if none of the characters in. 
  // Search from index Start. Return if not found 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;//  According to the jump out of the inner layer for The condition of the judgment, find is the end of the loop 
    }
    if(i==strLength)
      return npos;//  Not found, //  According to the jump out of the inner layer for The condition of the judgment, find is the end of the loop 
    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;//  If equal   This round of i It doesn't work. Go ahead 1 wheel 
      }
      if(j==str.strLength)
        break;//  According to the jump out of the inner layer for The condition of the judgment, find is the end of the loop 
    }
    if(i==strLength)
      return npos;//  Not found, //  According to the jump out of the inner layer for The condition of the judgment, find is the end of the loop 
    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)//  Slightly different from above. Look 1 I don't have to pay 
        break;
    }
    if(i==strLength)
      return npos;//  Not found, //  According to the jump out of the inner layer for The condition of the judgment, find is the end of the loop 
    return i;
  }
  // swap  They have to become, so no 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  It's still not easy to get. It's not ideal 
  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  function 
  int MyString::find(const char* str,size_t index)
  {
    assert(str!=NULL&&index<strLength);
    // kmp  In the getnext function 
    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  algorithm 
    k = index;
    j = 0;
    while(p_str[k]!='\0')
    {
      if(j==0 || p_str[k]==str[j])
      {
        k++;
        j++;
      }
      else
      {
        j = next[j];//  Eliminate pointer backtracking 
      }
      if(str[j] == '\0')// The match is successful 
        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  In the getnext function 

    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  algorithm 
    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];//  Eliminate pointer backtracking 
      }
      if(str.p_str[j] == '\0')// The match is successful , I don't know why I'm calling myself str[] Overloading always gives an error 
        return k-j;
    }
    if(str.p_str[j] == '\0')//  with 1 A string 
      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) Test function 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 << " Please enter the 1 Length less than 100 String of: for example 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);//  I don't know why I can't 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;
}

3: comprehension

(1) It took nearly 2 days to realize it. Rather than learn a lot from it, it is better to know string class again.

(2) I know this simple string class, string source code is far far away; But it has helped me to understand the string class better, or at least to use it more easily.

(3) simple realization of 1 string class, reference is STL source code, but their understanding is not deep enough, there are inevitably 1 error, please advise, thank you very much!

(4) Next step: Enter list


Related articles: