The implementation of simple string class in C + +

Source: Internet
Author: User
Tags assert shallow copy strlen


String

In My Learning life in C + + I found that the string class is very powerful, so we have to simulate it, and in the interview, simulate a string class is also a regular exam, but because of outside constraints we are not possible to simulate and the library string consistent ( The string in the C + + library is more powerful), so today we're only going to simulate the basic functions of string-constructors, copy constructors, destructors, assignment operator overloads, overloads of operator = +, overloads of operators [], C_STR (get a C-style character pointer, operable string ), Size,push_back,insert (deep copy), and the use of write-time copy Copy_on_write to implement the basic string class

The way of deep copy

Class String
{
Friend Ostream &operator<< (ostream &os,string &s);
Public
String (const char *str= ""); Full default constructor to resolve an empty string problem
String (const string &ps); Deep copy
String &operator= (string s);
String &operator+= (const char * s);
const char *C_STR () const//Get C-style character pointer
{
return _pstr;
}
Char &operator[] (size_t index)
{
return _pstr[index];
}
size_t Size () const
{
return _size;
}
void pushback (char c);
String &insert (size_t pos,const char *str);
String &operator= (String &s)
//{
cout<< "String &operator= (String &s)" <<endl;
if (this!= &s)
//  {
DELETE[]_PSTR;
_pstr=new Char[strlen (S._PSTR) +1];
strcpy (_PSTR,S._PSTR);
//  }
return *this;
//}
~string ()
{
cout<< "~string ()" <<endl;
if (_pstr!= NULL)
{
DELETE[]_PSTR;
_pstr=null;
_size=0;
_capacity=0;
}
}
Private
void checkcapacity (int count);
Private
int _size;
int _capacity;
Char *_pstr;
};

Ostream &operator<< (ostream &os,string &s)
{
os<<s._pstr;
return OS;
}

string::string (const char *STR)
: _size (strlen (str))
, _capacity (strlen (str) +1)
, _pstr (new char[_capacity])
{
cout<< "String ()" <<endl;
strcpy (_PSTR,STR);
}

string::string (const String &AMP;PS)
: _size (Ps._size)
, _capacity (strlen (PS._PSTR) +1)
, _pstr (new char[_capacity])
{
cout<< "string (const string &ps)" <<endl;
strcpy (_PSTR,PS._PSTR);
}

String &string::operator= (String s)
{
cout<< "String &operator= (String s)" <<endl;
Std::swap (_PSTR,S._PSTR);
Std::swap (_size,s._size);
Std::swap (_capacity,s._capacity);
return *this;
}

void string::checkcapacity (int count)
{
if (_size+count >= _capacity)
{
int _count= (2*_capacity) > (_capacity+count)? (2*_capacity):(_capacity+count);
Char *tmp=new Char[_count];
strcpy (TMP,_PSTR);
DELETE[]_PSTR;
_pstr=tmp;
_capacity=_count;
}
}

void String::P ushback (char c)
{
Checkcapacity (1);
_pstr[_size++]=c;
_pstr[_size]= ' ";
}

String &string::operator+= (const char * s)
{

Checkcapacity (strlen (s));
while (*s)
{
_pstr[_size++]=*s;
s++;
}
_pstr[_size]= ' ";
return *this;
}

String &string::insert (size_t pos,const char *str)
{
Char *tmp=new Char[strlen (_pstr+pos)];
strcpy (Tmp,_pstr+pos);
Checkcapacity (strlen (str));
while (*STR)
{
_PSTR[POS++]=*STR;
str++;
}
strcpy (_PSTR+POS,TMP);
return *this;
}

By testing the above code, especially when implementing the assignment operator overload, we used two ways, and it is worth mentioning the use of the swap function to implement the overload of the assignment operator (no reference can be passed when the argument is passed). Because the application of the SWAP function is based on the creation of a temporary variable and the temporary variable is scoped, the destructor is automatically called for Destruction (modern method)
How to test deep copy

void Text1 ()
{
String str1 ("Hello");
String str2 (STR1);
String STR3;
STR3=STR1;
cout<<str1<<endl;
cout<<str2<<endl;
cout<<str3<<endl;

Cout<<strlen (str1.   C_str ()) <<endl; 5

Str1[4]= ' W ';
cout<<str1<<endl; Hellw
}

void Text2 ()
{
String str1 ("ABCD");
cout<<str1<<endl;
Str1. Pushback (' e ');
Str1. Pushback (' F ');
Str1. Pushback (' G ');
Str1. Pushback (' H ');
Str1. Pushback (' I ');
cout<<str1<<endl;

Cout<<str1. Size () <<endl;
}

void Text3 ()
{
String str1 ("Hello");
String str2 ("Hello World");
String STR3 (STR2);
str1+= "";
str1+= "World";
cout<<str1<<endl;

Str2. Insert (6, "abc");
cout<<str2<<endl;
}

The method of realizing the deep copy does that Mulao have a more efficient way? Of course, that is the write-time copy, and we find that the copy constructor implemented in the deep copy of the above version also opens the space for new objects (to prevent the sequelae of shallow copies: a shallow copy is a value copy that causes two pointers to be in the same space, and a problem occurs when the space is released multiple times on the same block of space. That Mulao if we inherit the sequelae of a shallow copy-let multiple pointers point to the same space, at this point we just need to set a pointer variable to record the number of pointers to this space, as long as the contents of the pointer variable 1 we release the space otherwise let the counter minus 1, which is the main idea of the copy, Let's use the write-time copy method to implement a simple string class.
Methods of writing-time copying

How to copy when writing
Class String
{
Friend ostream& operator<< (ostream & os,string &s);
Public
String (const char *str= "")
: _str (new Char[strlen (str) +1+4])
{
cout<< "Construction" <<endl;
_str+=4;
* (int *) (_str-4) = 1;
strcpy (_STR,STR);
}
String (String &s)
{
cout<< "Copy Construction" <<endl;
++* ((int *) (s._str-4));
_STR=S._STR;
}
String &operator= (const string &s)
{
cout<< "assignment Statement" <<endl;
if (--* (int *) (_str-4) = = 0)
{
Delete[] (_str-4);
}
+ + (* (int *) (s._str-4));
_STR=S._STR;
return *this;
}
Char &operator[] (int index)//write-time copy
{
ASSERT (index >= 0 && Index < (int) strlen (_str));
if (* (int *) (_str-4) > 1)
{
--* (int *) (_str-4);
Char *tmp=new Char[strlen (_STR) +5];
strcpy (TMP+4,_STR);
Delete[] (_str-4);
_str=tmp+4;
* (int *) (_str-4) = 1;
}
return _str[index];
}
~string ()
{
cout<< "destructor" <<endl;
if (--* (int *) (_str-4) = = 0)
{
cout<< "Release" <<endl;
Delete[] (_str-4);
}
}
Private
Char *_str;
};
ostream& operator<< (ostream &os,string &s)
{
os<<s._str;
return OS;
}

Here we place the counter where the pointer points to the first four bytes of the data space
Test Cases:

void Test1 ()
{
String str1 ("ABCD");
cout<<str1<<endl;
String str2 (STR1);
cout<<str2<<endl;
String STR3;
STR3=STR1;
cout<<str3<<endl;
}

void Test2 ()
{
String str1 ("ABCD");
cout<<str1<<endl;
String str2;
STR2=STR1;
cout<<str2<<endl;
Str2[2]= ' W ';
cout<<str2<<endl;
}


A correct way of writing a string in an interview

You can define variables like the int type and support assignment and replication.
Can be used as the parameter type and return type of a function.
A type of element that can be used as a standard reservoir, that is, a vector/list/deque value_type. (used as a std::map key_type is a further requirement, this article Conlio).
In other words, your String allows the following code to compile and run through without any memory errors.

void foo (String x)
{
}

void Bar (const string& x)
{
}

String Baz ()
{
String ret ("World");
return ret;
}

int main ()
{
String S0;
String S1 ("Hello");
String S2 (S0);
String s3 = S1;
s2 = S1;

Foo (s1);
Bar (S1);
Foo ("temporary");
Bar ("temporary");
String S4 = Baz ();

Std::vector<string> Svec;
Svec.push_back (S0);
Svec.push_back (S1);
Svec.push_back (Baz ());
Svec.push_back ("Good job");
}
This article gives me the answer that I think is suitable for the interview, emphasize correctness and easy to realize (the Whiteboard is not wrong to write), do not emphasize efficiency. In a sense, it can be said that the time (speed) of space (code concise).

First select the data member, the simplest String has only one char* member variable. The benefits are easy to implement, and the downside is that some operations are more complex (such as size () will be linear time). There is only one char* Data_ member of the String designed for the interview to write code without error. and the rules invariant as follows: A valid string object's Data_ guarantee does not end with ' the ' for Null,data_, in order to facilitate the str* () series functions of the C language.

Second, decide which operations to support, construction, deconstruction, copy construction, assignment of these things are certain (formerly known as the Big Three, now called Copy control). If you drill deeper, C++11 's move construction and move assignments can also be available. In order to highlight the focus, this article does not consider operator[] such as the overload.

So the code is basically stereotyped:

#include <utility>
#include <string.h>

Class String
{
Public
String ()
: Data_ (new char[1])
{
*data_ = ' n ';
}

String (const char* STR)
: Data_ (new Char[strlen (str) + 1])
{
strcpy (Data_, str);
}

String (const string& RHS)
: Data_ (New Char[rhs.size () + 1])
{
strcpy (Data_, Rhs.c_str ());
}
/* Delegate Constructor in c++11
String (const string& RHS)
: String (Rhs.data_)
{
}
*/

~string ()
{
Delete[] Data_;
}

/* Traditional:
string& operator= (const string& RHS)
{
String tmp (RHS);
Swap (TMP);
return *this;
}
*/
string& operator= (String RHS)/Yes, Pass-by-value
{
Swap (RHS);
return *this;
}

C + + 11
String (string&& RHS)
: Data_ (Rhs.data_)
{
Rhs.data_ = nullptr;
}

string& operator= (string&& RHS)
{
Swap (RHS);
return *this;
}

Accessors

size_t size () const
{
return strlen (Data_);
}

Const char* C_STR () const
{
return data_;
}

void swap (string& rhs)
{
Std::swap (Data_, Rhs.data_);
}

Private
char* Data_;
};
Note the several points of the code:

Call new char[only in the constructor, call delete[in the destructor only.
The assignment operator uses the modern notation recommended by the C + + programming specification.
Each function has only one or two lines of code, no condition to judge.
The destructor does not have to check whether Data_ is NULL.
Constructor String (const char* str) does not check the legality of STR, which is a never-ending topic of contention. This is where STR is used in the initialization list, so it is meaningless to use ASSERT () in the function body.
This is probably the most concise String implementation.

Exercise 1: Add operator==, operator<, operator[] and other operator overloads.

Exercise 2: Implement a band int size_; The version of the member, in space to change time.

Exercise 3: Benefit from the right value reference and mobile semantics, in c++11, the performance of the direct insertion sort of String is higher than the C++98/03, test programming verification. (g++ 's standard library also uses this technique.) )

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.