It is quite complex to implement advanced String classes. Unlike C++ and C#, many modules need to solve their own memory problems, and how to improve the efficiency of memory space, whether calling a function triggers a copy constructor, and how to avoid duplicate memory release when triggering a copy constructor.
There are a few overloads left that have not been implemented, which will be written tomorrow, and the tests and instructions for this type need to be slowly added.
Declaration of class
Use char to implement a string class.
First, define a String class, then define internal member variables and member functions.
Content:
1.
Private Members | Explain |
---|---|
_str | Character string |
_size | String Length |
bool _SetString | Encapsulation Settings_ str and _ size |
_log | Temporary output information for testing, temporarily using string storage to save time, deleting all log-related code after class implementation |
public members | Explain |
---|---|
String | Constructors include default constructors, overloaded character arrays, overloaded copy constructors, and implementation of the move function |
~String | Destructor that frees memory when an object is destroyed |
String& operator = | Overload = operator and implementation of the move function, the difficulty is that it is written as its own recursion |
String operator + (not yet implemented) | Overload + operator, since the return is a temporary variable, const should be preceded and the function header does not require a reference |
String operator += | Overloaded + = operator, should be similar to overloaded = operator |
char& operator[] | Overload brackets to achieve c=s[0] and s[0]=c |
(not yet implemented) istream & operator >> | Overloaded input streams need to be studied for efficient writing and \r\n issues |
(Not yet implemented) ostream & operator < < | Overloaded output stream |
String* GetAddress | Return to your address |
String GetCopy() | Back to Clone |
String& GetOriginal() | Return to itself |
char* c_str() | Returns the address of the character array of the string itself, with const added to prevent memory from being modified |
int length() | Return string length |
class String { private: char* _str = NULL; size_t _size = 0; string _log; bool _SetString(const char* str); public: String(); String(const char *str); String(const String& other); String(String&& other) noexcept; // TODO: noexcept ~String(); // Overload = Operator String& operator =(const char* str); String& operator =(const String& other); String& operator =(String&& other) noexcept; // TODO: noexcept // Overload + operator, returns a temporary object, does not require a reference const String operator +(const char* str); const String operator +(const char ch); const String operator +(const String& other); // Overload+=Operator String operator +=(const char* str); String operator +=(const char ch); String operator +=(const String& other); // Overloaded brackets //char operator[](size_t index); char& operator[](size_t index); // Can implement str[0]='1' // Overload==Operator bool operator ==(const char* str); bool operator ==(const String& other); // Overloaded input-output stream friend istream& operator >> (istream& cin, String& str); friend ostream& operator << (ostream& cin, String& str); //Return to your address String* GetAddress() { return this; } // Back to Clone String GetCopy() { return *this; } // Return to itself String& GetOriginal() { return *this; } // Returns the address of the character array of the string itself, plus const to prevent modification const char* c_str() { return _str; } // Return string length int length() { return _size; } };
_SetString
bool String::_SetString(const char* str) { try { if (str == NULL) { _size = 0; _str = NULL; } else { if (_str != NULL) { delete[] _str; _str = NULL; } _size = strlen(str); _str = new char[_size + 1]; strcpy(_str, str); _str[_size] = '\0'; } } catch(...){ return false; } return true; }
Constructors and Destructors
String::String() { // Variables have been initialized at the declaration, and the destructor is only for output information _log = "String()"; cout << "String()" << endl; } String::String(const char* str) { _SetString(str); _log = "String(const char* str)"; cout << "String(const char* str)" << endl; } String::String(const String& other) { _SetString(other._str); _log = "String(const String& s)"; cout << "String(const String& s)" << endl; } // String's move implementation String::String(String&& other) noexcept { _size = other._size; _str = other._str; other._size = 0; // This way, other. _will not be recycled while destructing the other. Memory space pointed to by STR other._str = NULL; } String::~String() { cout << "~String" << _log << endl; if (_str != NULL) { delete _str; _str = NULL; } }
Items to be tested
String s1(); String s2("123"); String s3(s2); String s4 = s2; // s4 is not instantiated, here equivalent to String s4(s2) String s5; s5 = s2; // Trigger = Overload
If no overload=, an error will be reported "trying to reference a deleted function", so it must be overloaded
The conditions that trigger an attempt to reference a deleted function:
- Contains a non-static const member variable
- reference member variable with non-static state
- Contains member variables that cannot be copied
- Contains a base class that cannot be copied
- Contains a user-defined move constructor or a move assignment function
See other blogs explaining move (not understood yet):
std::move can force a left-value reference to a right-value reference, except that the transition state does not transfer memory, which is equivalent to static_ Cast <T &&>
The function parameter T & & is a right-value reference to a template type parameter that can be matched to any type of argument by reference collapse
Overload = Operator
String& String::operator=(const char* str) { cout << "operator=(const char* str)" << endl; _SetString(str); return *this; } String& String::operator=(const String& other) { cout << "operator=(const String& other) self" << endl; // Check Self-Assignment Points if (this != &other) { // Release memory, allocate new space _SetString(other._str); } return *this; } String& String::operator=(String&& other) noexcept { cout << "operator=(String&& other)" << endl; // Check Self-Assignment Points if (this != &other) { _size = other._size; _str = other._str; other._size = 0; other._str = NULL; } return *this; }
Here are the wrong writings, which go into an infinite recursive loop. I naturally assume that *this=String(...) will make the copy I want, but it will trigger the third copy and go into an infinite loop
String& String::operator=(const char* str) { cout << "operator=(const char* str)" << endl; *this = String(str); return *this; } String& String::operator=(const String& other) { cout << "operator=(const String& other)" << endl; *this = String(other); return *this; } String& String::operator=(String&& other) noexcept { cout << "operator=(String&& other)" << endl; *this = String(other); return *this; }
To be tested
String s("123"); String s2("456"); String *s3; s3 = &s2; // Pointer reference String s4; s4 = CreateString("111"); // Trigger the third = overload cout << endl<< s[0] <<endl; cout << endl<< s2[0] <<endl; s[0]='9'; cout << endl << s[0] << endl; cout << endl << s2[0] << endl; cout << endl << (*s3)[0] << endl;
Overload+Operator
Overload+=Operator
Overloaded brackets
char& String::operator[](size_t index) { return _str[index]; }