C++_String-Simulationsimplementierung zum Hinzufügen, Löschen, Überprüfen und Ändern

Vorwort

Dieser Blog implementiert nur die Zeichenfolgenspeicherung von Zeichen. Gleichzeitig implementiert der Blogger aufgrund des unvernünftigen Designs der C++-String-Bibliothek nur einige der gängigsten Schnittstellen zum Hinzufügen, Löschen, Überprüfen und Ändern!
Die im Folgenden aufgeführten Schnittstellen basieren auf dem folgenden Framework:

namespace achieveString
{
    
    
	class string
	{
    
    

	private:
		char* _str;
		size_t _capacity;
		size_t _size;
	};
}

1. String-Standardkonstruktion, Destruktor, Kopierkonstruktion, Zuweisungsüberladung

1.1 Standardkonstruktion

Der Blogger stellt nur die folgenden Schnittstellen zur Verfügung:Keine Parameter und parametrisierte Standardstruktur

//无参默认构造
string()
	:_str(new char[1]{
    
    '\0'})
	,_capacity(0)
	,_size(0)
{
    
     }
//带参默认构造
string(const char* str = "")
	:_capacity(strlen(str))
	,_size(_capacity)
{
    
    
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

Kleine Tipps:

  1. In der C++-String-Standardbibliothek hat die parameterlose Konstruktion kein Leerzeichen von 0, sondern wird direkt auf einen Nullzeiger gesetzt. Öffnen Sie stattdessen ein Byte und speichern Sie „\0“. (C++ unterstützt das Erstellen eines Objekts ohne Parameter und das spätere direkte Einfügen von Daten, was diesen Punkt auch verdeutlicht.)
  2. Da der C++-Konstruktor die Initialisierungsliste verwendet, unabhängig davon, ob sie geschrieben ist oder nicht, verwendet der Blogger hier auch die Initialisierungsliste.
  3. Im String enthalten weder _capacity noch _size einen Nullzeiger, daher muss die Konstruktion mit Parametern einen zusätzlichen Platz zum Speichern von „\0“ öffnen.

1.2 Destruktor

~string()
{
    
    
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

1.3 Konstruktion kopieren

Traditionelles Schreiben:

string(const string& s)
{
    
    
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

Moderne Schreibmethode: Der Kern der modernen Schreibmethode besteht darin, die Arbeit des Kopierens von Daten anderen zu überlassen und schließlich die Ergebnisse auszutauschen.

//交换
void swap(string& s)
{
    
    
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

//现代写法
string(const string& s)
	:_str(nullptr)
	,_size(0)
	,_capacity(0)
{
    
    
	string tmp(s._str);
	swap(tmp);
}

Tipps: Beim modernen Schreiben bedeutet die Kopierkonstruktion, dass die Daten leer initialisiert werden müssen. Der Grund dafür ist, dass der Compiler in C++ keine integrierten Typen verarbeitet (einige Compiler wie vs2019 tun dies), was bedeutet, dass _str ein Zufallswert ist, der auf ein beliebiges Leerzeichen zeigt. Beim Aufruf des Destruktors wird ein Fehler gemeldet.

1.4 Aufgabenüberlastung

Aufgabenüberlastung wird auch in traditionelles Schreiben und modernes Schreiben unterteilt.
Traditionelles Schreiben:

string& operator=(const string& s)
{
    
    
	if (this != &s)
	{
    
    
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;
	}
	return *this;
}

Modernes Schreiben:

//现代写法
//法一
/*string& operator=(const string& s)
{
	if (this != &s)
	{
		string tmp(s._str);
		swap(tmp);
	}
	return *this;
}*/

//法二
string& operator=(string tmp)
{
    
    
	swap(tmp);
	return *this;
}

2. Iterator und Bereich für

In C++ wirdder Bereich für durch Iteratoren auf der untersten Ebene implementiert. Solange der Iterator implementiert ist, wird der Bereich für unterstützt.
Der Iterator ähnelt einem Zeiger. Der Iterator kann als Verallgemeinerung des Zeigers betrachtet werden. Er stellt zeigerähnliche Funktionen bereit und kann Dereferenzierungsoperationen, Zeigeroperationen usw. ausführen.
 
Das Folgende stellt konstante Iteratoren und nicht konstante Iteratoren bereit:

typedef char* iterator;
const typedef char* const_iterator;

	iterator begin()
	{
    
    
		return _str;
	}
	iterator end()
	{
    
    
		return _str + _size;
	}

	const_iterator begin() const
	{
    
    
		return _str;
	}
	const_iterator end() const
	{
    
    
		return _str + _size;
	}

3. Elementbezogen: Operator[]

Hier stellen wir wie in der Bibliothek die folgenden zwei Versionen zur Verfügung

//可读可写
char operator[](size_t pos)
{
    
    
	assert(pos < _size);
	return _str[pos];
}
//只读
const char operator[](size_t pos)const
{
    
    
	assert(pos < _size);
	return _str[pos];
}

4. Kapazitätsbezogen: Größe, Größenänderung, Kapazität, Reserve

4,1 Größe、Kapazität

size_t size()const
{
    
    
	return _size;
}
size_t capacity()const
{
    
    
	return _capacity;
}

4.2 Reserve

In C++ schrumpfen wir im Allgemeinen nicht.
Daher muss bei der Implementierung der Reserve (Kapazität wird an n angepasst) zunächst ermittelt werden, ob die Zielkapazität n größer als die aktuelle Kapazität ist. Wenn es kleiner als 0 ist, wird keine Verarbeitung durchgeführt, andernfalls öffnen Sie zuerst n+1 Speicherplatz (der zusätzliche Speicherplatz wird zum Speichern von „\0“ verwendet) und kopieren Sie dann das Original Daten in den neuen Bereich (strcpy '\0' wird zusammen kopiert). Geben Sie dann den alten Speicherplatz frei, lassen Sie _str auf den neuen Speicherplatz zeigen und aktualisieren Sie gleichzeitig _capacity.

void reserve(size_t n)
{
    
    
	if (n > _capacity)
	{
    
    
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

4.3 Größe ändern

Die Größenänderung auf die Zielgröße wird in die folgenden drei Situationen unterteilt:
Fügen Sie hier eine Bildbeschreibung ein

  1. Wenn n<_size, ändern Sie einfach die Daten an der Adresse mit dem Index n in „\0“.
  2. In anderen Fällen kümmern wir uns direkt darum. Verwenden Sie die Funktion „reserve()“ direkt wieder, um _capacity auf n zu erweitern. Verwenden Sie dann, um alle Daten in [_size, n) in ch zu initialisieren. (Hier gibt der Blogger ch einen Anfangswert von „\0“, aber ch ist nicht unbedingt „\0“, daher muss die Adresse bei Index n mit „\0“ initialisiert werden.)
void resize(size_t n, char ch='\0')
{
    
    
	if (n <= _size)
	{
    
    
		_str[n] = '\0';
		_size = n;
	}
	else
	{
    
    
		reserve(n);
		while (_size < n)
		{
    
    
			_str[_size] = ch;
			_size++;
		}
		_str[_size] = '\0';
	}
}

5. Datenbezogen: push_bach, append, Operator+=, insert, erase

5.1 Schwanzeinfügung: push_back

Beim Einfügen von Schwanz wird zunächst die Erweiterung überprüft und dann Daten eingefügt

void push_back(char ch)
{
    
    
	//扩容
	if (_size == _capacity)
	{
    
    
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	//插入数据
	_str[_size] = ch;
	_size++;
	_str[_size] = '\0';

}

5.2 Zeichenfolge am Ende des Anhängens einfügen

void append(const char* str)
{
    
    
	size_t len = strlen(str);
	if (_size + len > _capacity)//扩容
	{
    
    
		reserve(_size + len);
	}
	strcpy(_str + _size, str);
	_size += len;
}

5.3 Operator+=() Zeichen, Zeichenfolge

Operator+=() Zeichen und Zeichenfolgen können push_back direkt wiederverwenden und anhängen.

string& operator+=(char ch)
{
    
    
	push_back(ch);
	return *this;
}

string& operator+=(const char* str)
{
    
    
	append(str);
	return *this;
}

5.4 Einfügen von Zeichen und Zeichenfolgen

5.4.1 Einfügen von Einfügezeichen (zur Erinnerung: Der Blogger beginnt mit „\0“, sodass alle kopierten Daten bei „\0“ beginnen, sodass „\0“ nicht separat verarbeitet werden muss.)

insertDie Logik zum Einfügen von Zeichen ist immer noch sehr einfach.
Bestimmen Sie zunächst, ob beim Einfügen von Zeichen eine Erweiterung erforderlich ist. Dann werden alle Daten, beginnend mit dem Index pos, der Reihe nach rückwärts verschoben. Zum Schluss setzen Sie das einzufügende Zeichen an die Position.

Der häufigste Fehler für Anfänger

Aber für Anfänger scheint es nicht einfach zu sein. . . . . .
Hier sind die häufigsten Fehler, die Anfänger machen:

void insert(size_t pos, char ch)
{
    
    
	assert(pos <= _size);
	//扩容
	if (_size == _capacity)
	{
    
    
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	//挪动数据
	size_t end = _size;
	while (end >= pos)
	{
    
    
		_str[end+1] = _str[end];
		end--;
	}
	_str[pos] = ch;
	_size++
}

ist das richtig? Die Antwort ist falsch.

Unter der Annahme, dass am Anfang ein Zeichen eingefügt wird, wird end nach dem Vergleich mit pos theoretisch auf -1 reduziert (d. h. 0). Beim nächsten Schleifenbedingungsvergleich schlägt dies fehl und die Schleife wird verlassen.
Leider ist end vom Typ size_t, immer >=0, was zu einer Endlosschleife führt.

Der Blogger gibt hier zwei Lösungen:

  1. Konvertieren Sie pos in einen Ganzzahltyp.
void insert(size_t pos, char ch)
{
    
    
	assert(pos <= _size);
	//扩容
	if (_size == _capacity)
	{
    
    
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	//挪动数据
	int end = _size;
	while (end >= (int)pos)
	{
    
    
		_str[end+1] = _str[end];
		end--;
	}
	_str[pos] = ch;
	_size++
}

2. Beginnen Sie am Ende mit der letzten Ziffer der letzten Daten und verschieben Sie die vorherigen Daten jedes Mal an die aktuelle Position. Am Ende wird die bedingte Beurteilung in end>pos umgewandelt, und es wird keine Endlosschleife geben.
Fügen Sie hier eine Bildbeschreibung ein

void insert(size_t pos, char ch)
{
    
    
	assert(pos <= _size);
	//扩容
	if (_size == _capacity)
	{
    
    
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	//挪动数据
	size_t end = _size+1;
	while (end > pos)
	{
    
    
		_str[end] = _str[end-1];
		end--;
	}
	//插入数据,更新_size
	_str[pos] = ch;
	_size++;
}

5.4.2 Zeichenfolge einfügen

insert hat auch das gleiche Problem und die Idee ist dieselbe. Der Blogger hat den Code einfach direkt angegeben.
Methode 1:

void insert(size_t pos, const char* str)
{
    
    
	int len = strlen(str);
	if (_size + len > _capacity)
	{
    
    
		reserve(_size + len);
	}

	int end = _size;
	while (end >= (int)pos)
	{
    
    
		_str[end + len] = _str[end];
		end--;
	}
	strncpy(_str + pos, str, len);
	_size += len;
}

Houji:

void insert(size_t pos, const char* str)
{
    
    
	int len = strlen(str);
	if (_size + len > _capacity)
	{
    
    
		reserve(_size + len);
	}
	
	size_t end = _size+1;
	while (end > pos)
	{
    
    
		_str[end + len-1] = _str[end-1];
		end--;
	}
	strncpy(_str + pos, str, len);
	_size += len;
}

5,5 löschen

Es gibt zwei Fälle von Löschung:

  1. Ab pos ist die Zeichenfolge, die die Anzahl der zu löschenden Daten überschreitet, alle Daten nach pos. (Setzen Sie einfach die Daten an pos direkt auf '\0')
  2. Ab pos überschreitet die Anzahl der zu löschenden Daten die Zeichenfolge nicht. Sie müssen also nur alle Daten nach der Pos+Len-Position nach vorne verschieben und die Originaldaten ab der Pos-Position überschreiben.
void erase(size_t pos, size_t len = npos)
{
    
    
	if (len==npos || pos + len >= _size)
	{
    
    
		//有多少,删多少
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
    
    
		size_t begin = pos + len;
		while (begin <= _size)
		{
    
    
			_str[begin - len] = _str[begin];
			begin++;
		}
		_size -= len;
	}
}

6. Überladung relationaler Operatoren: <, ==, <=, >, >=, !=

bool operator<(const string& s) const
{
    
    
	return strcmp(_str, s._str) < 0;
}

bool operator==(const string& s) const
{
    
    
	return strcmp(_str, s._str) == 0;
}

bool operator<=(const string& s) const
{
    
    
	return *this < s || *this == s;
}

bool operator>(const string& s) const
{
    
    
	return !(*this <= s);
}

bool operator>=(const string& s) const
{
    
    
	return !(*this < s);
}

bool operator!=(const string& s) const
{
    
    
	return !(*this == s);
}

7. Suchen Sie nach Zeichen, Zeichenfolgen und Unterzeichen

7.1 find findet Zeichen

size_t find(char ch, size_t pos = 0)
{
    
    
	for (size_t i = pos; i < _size; i++)
	{
    
    
		if (_str[i] == ch)
		{
    
    
			return i;
		}
	}

	return npos;
}

7.2 Suchzeichenfolge finden

size_t find(const char* sub, size_t pos = 0)
{
    
    
	const char* p = strstr(_str + pos, sub);
	if (p)
	{
    
    
		return p - _str;
	}
	else
	{
    
    
		return npos;
	}
}

7.3 strsub( )-Simulationsimplementierung

Die strsub-Ziellänge liegt möglicherweise außerhalb des zulässigen Bereichs für die Zeichenfolge oder auch nicht. Aber egal wie die Situation ist, am Ende müssen die Daten kopiert werden. Hier können wir also zunächst die tatsächliche Länge von len berechnen und dann die Daten kopieren.

string substr(size_t pos, size_t len = npos)const
{
    
    
	string s;
	size_t end = pos + len;
	//目标字符越界string,更新len
	if (len == npos || end >= _size)
	{
    
    
		len = _size - pos;
		end = _size;
	}
	
	//拷贝数据
	s.reserve(len);
	for (size_t i = pos; i < end; i++)
	{
    
    
		s += _str[i];
	}

	return s;
}

8. Stream-Einfügung und Stream-Extraktion (<<, >>) (außerhalb der String-Klasse implementiert)

8.1 Stream-Einfügung <<

Da wir bereits früher Iteratoren implementiert haben, ist Range for der einfachste Weg

ostream& operator<<(ostream& out, const string& s)
{
    
    
	/*for (size_t i = 0; i < s.size(); i++)
	{
		out << s[i];
	}*/
	for (auto ch : s)
		out << ch;

	return out;
}

8.1 Stream-Extraktion>>

Stream-Extraktion ist etwas Besonderes. Alle Originaldaten müssen vor der Stream-Extraktion gelöscht werden. Da >> gleichzeitig das Nullzeichen und das Zeilenumbruchzeichen () nicht erhalten kann (beide werden als Intervalle zwischen mehreren Werten verwendet), wird der Stream direkt in das Ostream-Objekt extrahiert und kann nicht beendet werden. (Ähnlich wie bei scanf in der C-Sprache spielen Zeilenumbrüche und Nullzeichen nur eine Rolle bei der Beurteilung des Endes, scanf kann sie jedoch nicht abrufen.)
Hier ruft der Blogger also direkt get( im istream-Objekt) auf. Funktion. (Ähnlich der getchar()-Funktion in der C-Sprache)
Detaillierte Dokumentation erhalten
Fügen Sie hier eine Bildbeschreibung ein

class string
{
    
    
	void clear()
	{
    
    
		_str[0] = '\0';
		_size = 0;
	}
private:
	char* _str;
	size_t _capacity;
	size_t _size;
};

istream& operator>>(istream& in, string& s)
	{
    
    
		s.clear();
		char ch;
		//in >> ch;
		ch = in.get();

		while (ch != ' ' && ch != '\n')
		{
    
    
			s += ch;
			//in >> ch;
			ch = in.get();
		}
		return in;
	}

Obwohl die obige Methode das Ziel erreichen kann. Es gibt jedoch noch ein weiteres Problem: Jedes Mal, wenn Daten eingefügt werden, besteht das Problem der Skalierbarkeit. Wie kann man es also optimieren?

Optimierung

Eine Möglichkeit besteht darin, Reserve () aufzurufen, um den Speicherplatz im Voraus zu öffnen. Dies führt jedoch zu einem anderen Problem: Wenn es zu groß geöffnet wird, wird Platz verschwendet. Wenn es zu klein geöffnet wird, tritt auch das Problem auf Erweiterung.
Hier übernimmt der Blogger also die gleiche Idee wie die zugrunde liegende Implementierung von vs: Öffnen Sie zuerst ein Array (einschließlich „\0“, nehmen Sie 16 als Beispiel). Wenn die Anzahl der Daten kleiner als 16 ist, wird die Zeichenfolge im Array gespeichert. Wenn die Anzahl der Daten größer oder gleich 16 ist, werden die Daten in dem Bereich gespeichert, auf den _str zeigt.
Dies ist eine Idee des Austauschs von Raum gegen Zeit und kann auch das Problem der Speicherfragmentierung wirksam reduzieren.

class string
{
    
    
	void clear()
	{
    
    
		_str[0] = '\0';
		_size = 0;
	}
private:
	char* _str;
	size_t _capacity;
	size_t _size;
};

istream& operator>>(istream& in, string& s)
{
    
    
	s.clear();

	char buff[16];
	size_t i = 0;

	char ch;
	ch = in.get();
	while (ch != ' ' && ch != '\n')
	{
    
    
		buff[i++] = ch;
		if (i == 16)
		{
    
    
			buff[i] = '\0';
			s += buff;
			i = 0;
		}
	ch = in.get();
	}

	if (i != 0)
	{
    
    
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

9. Alle Codes

namespace achieveString
{
    
    
	class string
	{
    
    
	public:
	typedef char* iterator;
	const typedef char* const_iterator;

	iterator begin()
	{
    
    
		return _str;
	}
	iterator end()
	{
    
    
		return _str + _size;
	}

	const_iterator begin() const
	{
    
    
		return _str;
	}
	const_iterator end() const
	{
    
    
		return _str + _size;
	}

		//构造函数
		/*string()
			:_str(new char[1]{'\0'})
			,_capacity(0)
			,_size(0)
		{ }*/
		string(const char* str = "")
			:_capacity(strlen(str))
			, _size(_capacity)
		{
    
    
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		const char* c_str() const
		{
    
    
			return _str;
		}

		//析构函数
		~string()
		{
    
    
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		//拷贝构造
		/*string(const string& s)
		{
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}*/

		//交换
		void swap(string& s)
		{
    
    
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		//现代写法
		string(const string& s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
    
    
			string tmp(s._str);
			swap(tmp);
		}

		// 赋值重载
		/*string& operator=(const string& s)
		{
			if (this != &s)
			{
				char* tmp = new char[s._capacity + 1];
				strcpy(tmp, s._str);
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}*/
		//现代写法
		//法一
		/*string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s._str);
				swap(tmp);
			}
			return *this;
		}*/
		//法二
		string& operator=(string tmp)
		{
    
    
			swap(tmp);
			return *this;
		}

		//可读可写
		char operator[](size_t pos)
		{
    
    
			assert(pos < _size);
			return _str[pos];
		}
		//只读
		const char operator[](size_t pos)const
		{
    
    
			assert(pos < _size);
			return _str[pos];
		}

		size_t size()const
		{
    
    
			return _size;
		}
		size_t capacity()const
		{
    
    
			return _capacity;
		}

		bool empty()const
		{
    
    
			return _size == 0;
		}

		void reserve(size_t n)
		{
    
    
			if (n > _capacity)
			{
    
    
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}

		void resize(size_t n, char ch='\0')
		{
    
    
			if (n <= _size)
			{
    
    
				_str[n] = '\0';
				_size = n;
			}
			else
			{
    
    
				reserve(n);
				while (_size < n)
				{
    
    
					_str[_size] = ch;
					_size++;
				}
				_str[_size] = '\0';
			}
		}

		void push_back(char ch)
		{
    
    
			//扩容
			if (_size == _capacity)
			{
    
    
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//插入数据
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		
		}

		void append(const char* str)
		{
    
    
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
    
    
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
		}

		string& operator+=(char ch)
		{
    
    
			push_back(ch);
			return *this;
		}

		string& operator+=(const char* str)
		{
    
    
			append(str);
			return *this;
		}
		
		void insert(size_t pos, char ch)
		{
    
    
			assert(pos <= _size);
			//扩容
			if (_size == _capacity)
			{
    
    
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//挪动数据
			size_t end = _size+1;
			while (end > pos)
			{
    
    
				_str[end] = _str[end-1];
				end--;
			}
			//插入数据,更新_size
			_str[pos] = ch;
			_size++;
		}
		void insert(size_t pos, const char* str)
		{
    
    
			int len = strlen(str);
			if (_size + len > _capacity)
			{
    
    
				reserve(_size + len);
			}

			//法一
			/*int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				end--;
			}
			strncpy(_str + pos, str, len);
			_size += len;*/

			//法二
			size_t end = _size+1;
			while (end > pos)
			{
    
    
				_str[end + len-1] = _str[end-1];
				end--;
			}
			strncpy(_str + pos, str, len);
			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
    
    
			if (len==npos || pos + len >= _size)
			{
    
    
				//有多少,删多少
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
    
    
				size_t begin = pos + len;
				while (begin <= _size)
				{
    
    
					_str[begin - len] = _str[begin];
					begin++;
				}
				_size -= len;
			}
		}
		bool operator<(const string& s)const
		{
    
    
			return strcmp(_str, s._str) < 0;
		}

		bool operator==(const string& s)const
		{
    
    
			return strcmp(_str, s._str) == 0;
		}

		bool operator<=(const string& s)const
		{
    
    
			return *this == s && *this < s;
		}

		bool operator>(const string& s)const
		{
    
    
			return !(*this <= s);
		}

		bool operator>=(const string& s)const
		{
    
    
			return !(*this < s);
		}

		bool operator!=(const string& s)const
		{
    
    
			return !(*this == s);
		}

		size_t find(char ch, size_t pos = 0)
		{
    
    
			for (size_t i = pos; i < _size; i++)
			{
    
    
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}

		size_t find(const char* sub, size_t pos = 0)
		{
    
    
			const char* p = strstr(_str + pos, sub);
			if (p)
			{
    
    
				return p - _str;
			}
			else
			{
    
    
				return npos;
			}
		}

		string substr(size_t pos, size_t len = npos)
		{
    
    
			string s;
			size_t end = pos + len;
			if (len == npos || end >= _size)
			{
    
    
				len = _size - pos;
				end = _size;
			}
			
			s.reserve(len);
			for (size_t i = pos; i < end; i++)
			{
    
    
				s += _str[i];
			}

			return s;
		}
		void clear()
		{
    
    
			_str[0] = '\0';
			_size = 0;
		}
	private:
		char* _str;
		size_t _capacity;
		size_t _size;

		//const static size_t npos = -1;  // C++支持const整型静态变量在声明时给值初始化,但不建议
		//const static double npos = 1.1;  // 不支持

		const static size_t npos;
	};
	const size_t string::npos = -1;

	ostream& operator<<(ostream& out, const string& s)
	{
    
    
		/*for (size_t i = 0; i < s.size(); i++)
		{
			out << s[i];
		}*/
		for (auto ch : s)
			out << ch;

		return out;
	}

	//istream& operator>>(istream& in, string& s)
	//{
    
    
	//	s.clear();
	//	char ch;
	//	//in >> ch;
	//	ch = in.get();

	//	while (ch != ' ' && ch != '\n')
	//	{
    
    
	//		s += ch;
	//		//in >> ch;
	//		ch = in.get();
	//	}
	//	return in;
	//}

	istream& operator>>(istream& in, string& s)
	{
    
    
		s.clear();

		char buff[16];
		size_t i = 0;

		char ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
    
    
			buff[i++] = ch;
			if (i == 16)
			{
    
    
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}

		if (i != 0)
		{
    
    
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}
}

Guess you like

Origin blog.csdn.net/Zhenyu_Coder/article/details/134519404