文字列クラス(c ++)の説明とシミュレーションの実装

コンテンツ

1.STLとは

1.STLとは

2.STLバージョン

3.STLの6つのコンポーネント

4.STLの欠陥

2.文字列とは

 1.なぜ文字列を学ぶのですか?

2.ドキュメントでの文字列の導入

3.まとめ

 3.文字列クラスのメンバー関数

1.文字列クラスオブジェクトの一般的な構成

 2.文字列クラスオブジェクトの容量操作

3.文字列クラスオブジェクトのアクセスおよびトラバーサル操作

 4.文字列クラスオブジェクトの変更操作

5.文字列クラスの非メンバー関数

 4.文字列クラスのシミュレーション実装

1.入力および出力関数のオーバーロードのメンバー変数およびイテレーター宣言とフレンド宣言

2.文字列クラスオブジェクトの一般的な構成

3.イテレータ(ポインタと考えてください)

4.文字列クラスオブジェクトの容量操作

5.文字列クラスオブジェクトの変更操作

6.文字列クラスオブジェクトへのアクセス

7.文字列クラスの非メンバー関数

8.文字列クラスの文字列に関連する特別な操作

9.文字列オブジェクトの入出力関数のオーバーロード

5.文字列クラスを使用する際に注意が必要な事項

1.実際の文字列クラスの内部メンバー変数

2.実際の文字列クラス拡張メソッド(コンパイラが異なれば異なる場合があります)

3.容量拡張のために予備を使用するプロセス

4.[]とatの違い

5.コピーコンストラクタと代入演算子のオーバーロードに関する詳細

6.文字列和ベクトル<char>


1.STLとは

文字列をシミュレートして実装するには、まず文字列とは何かを理解する必要があり、文字列を理解するには、まずSTLを理解する必要があります。

1.STLとは

STLのフルネームは標準テンプレートlibaray-C++標準ライブラリの重要な部分である標準テンプレートライブラリです。これは再利用可能なコンポーネントライブラリであるだけでなく、データ構造とアルゴリズムを含むソフトウェアフレームワークでもあります。データ構造はカプセル化されています。テンプレートで、いくつかの一般的で柔軟なアルゴリズムを提供します。

2.STLバージョン

①。オリジナルバージョンHP研究所のAlexanderStepanovとMengLeeによって完成されたオリジナルバージョンは、オープンソースの精神で、誰でもこれらのコードを無料で使用、コピー、変更、配布、および商業的に使用できることを宣言しています。唯一の条件は、元のバージョンであるHPバージョン(すべてのSTL実装の祖先)と同様にオープンソースとして使用する必要があることです。

②.PJバージョンはPJPlaugerによって開発され、HPバージョンから継承され、Windows Visual C ++によって採用され、開示または変更できません。欠陥:読みやすさの低さ、奇妙な記号の命名。

③.RWバージョンはRougeWageCompanyによって開発され、HPバージョンから継承され、C ++ Builderによって採用され、公開または変更できず、一般的に読みやすくなっています。

④。SGIバージョンはSiliconGraphicsComputer Systems、Incによって開発され、HPバージョンから継承されました。GCC(Linux)に採用されており、移植性に優れ、公開、変更、販売も可能です。ネーミングスタイルやプログラミングスタイルの観点から、非常に読みやすくなっています。後でSTLを学ぶためにソースコードの一部を読みます。このバージョンが主なリファレンスです。

3.STLの6つのコンポーネント

シミュレートする文字列クラスは、コンテナの一部です。 

4.STLの欠陥

①.STLライブラリの更新が遅すぎる。これは深刻な苦情です。最後のバージョンはC++98で、途中のC++03は基本的に改訂されました。Cのリリースから13年が経過しました。 ++11。更新します。

②。STLは現在スレッドセーフをサポートしていません。並行環境で自分自身をロックする必要があり、ロックの粒度は比較的大きいです。

③。STLの極端な効率の追求は、型抽出やイテレータ抽出などの複雑な内部構造につながります。

4. STLを使用すると、コード拡張の問題が発生します。たとえば、vector <int> / vector <double> / vector <char>を使用すると、複数のコードが生成されます。もちろん、これはテンプレート構文自体が原因です。 。

2.文字列とは

 1.なぜ文字列を学ぶのですか?

C言語の文字列:C言語では、文字列は「\ 0」で終わる文字のコレクションです。操作の便宜のために、一部のstrシリーズライブラリ関数がC標準ライブラリで提供されていますが、これらのライブラリ関数は文字列。これは分離されていますが、これはOOP(オブジェクト指向プログラミング)の概念に沿ったものではなく、基になるスペースはユーザーが管理する必要があり、注意しないと範囲外にアクセスされる可能性があります。

したがって、日常業務では、文字列クラスは基本的に単純さ、利便性、速度のために使用され、Cライブラリの文字列操作関数を使用する人はほとんどいません。

2.ドキュメントでの文字列の導入

①。文字列は、文字のシーケンスを表すクラスです。

②。標準文字列クラスはそのようなオブジェクトのサポートを提供し、そのインターフェイスは標準文字コンテナのインターフェイスと似ていますが、特にシングルバイト文字列を操作するための設計機能が追加されています。

③。文字列クラスはcharを使用します(つまり、文字タイプとして、デフォルトのchar_traitsとアロケータタイプを使用します(テンプレートの詳細については、basic_stringを参照してください))。

4. stringクラスはbasic_stringテンプレートクラスのインスタンスであり、charを使用してbasic_stringテンプレートクラスをインスタンス化し、char_traitsとallocatorをbasic_stringのデフォルトパラメータとして使用します(テンプレートの詳細については、basic_stringを参照してください)。

⑤。このクラスは、使用されるエンコーディングとは関係なくバイトを処理することに注意してください。マルチバイトまたは可変長文字(UTF-8など)のシーケンスを処理するために使用される場合、このクラスのすべてのメンバー(長さやサイズなど)とその(実際にエンコードされた文字ではなく)バイトを操作します。

注:文字列クラスを使用する場合は、#include<string>をインクルードしてnamespacestdを使用する必要があります。

3.まとめ

 3.文字列クラスのメンバー関数

注:ここでは、すべてではなく、いくつかの一般的に使用されるメンバー関数についてのみ説明します。

1.文字列クラスオブジェクトの一般的な構成

 2.文字列クラスオブジェクトの容量操作

知らせ:

1. size()メソッドとlength()メソッドの基本的な実装原理はまったく同じです。size()を導入する理由は、他のコンテナーのインターフェースと一貫性を保つためです。一般に、size()が基本的に使用されます。

2. clear()は、基になるスペースサイズを変更せずに、文字列内の有効な文字をクリアするだけです。

3. resize(size_t n)とresize(size_t n、char c)はどちらも、文字列内の有効な文字数をnに変更します。違いは、文字数が増えると、resize(n)が文字数を次のように埋めるということです。 0.追加の要素スペースresize(size_t n、char c)は、追加の要素スペースを文字cで埋めます。注:resizeで要素数を変更する場合、要素数を増やすと、サイズが変更される可能性があります。基礎となる容量。要素の数を減らしても、基礎となるスペースの合計サイズは変更されません。

4. reserved(size_t res_arg = 0):有効な要素の数を変更せずに文字列のスペースを予約します。予約パラメーターが文字列の基になるスペースの合計サイズよりも小さい場合、予約によって容量は変更されません。

3.文字列クラスオブジェクトのアクセスおよびトラバーサル操作

 4.文字列クラスオブジェクトの変更操作

知らせ:

1.文字列の最後に文字を追加する場合、s.push_back(c)/ s.append(1、c)/ s + ='c'の3つのメソッドは類似しています。一般に、+=の操作は文字列クラスは比較に使用されます複数の+=操作は、単一の文字を連結できるだけでなく、文字列を連結することもできます。

2.文字列を操作する場合、入力する文字数を概算できる場合は、最初に予約してスペースを予約できます。

5.文字列クラスの非メンバー関数

 4.文字列クラスのシミュレーション実装

1.入力および出力関数のオーバーロードのメンバー変数およびイテレーター宣言とフレンド宣言

//输入输出函数重载
        friend ostream& operator<<(ostream& _cout, const lzStr::string& s);
        friend istream& operator>>(istream& _cin, lzStr::string& s);
//迭代器声明
        typedef char* iterator;
//成员变量      
        char* _str;
        size_t _capacity;
        size_t _size;

2.文字列クラスオブジェクトの一般的な構成

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

		string(const string& s)//拷贝构造函数
			:_str(nullptr)
		{
			lzStr::string tempStr(s._str);
			swap(tempStr);
		}

		string& operator=(const string& s) {//赋值运算符重载
			if (this != &s) {
				lzStr::string tempStr(s._str);
				this->swap(tempStr);
			}
			return *this;
		}

		~string() {//析构函数
			if (_str != nullptr) {
				delete[] _str;
				_str = nullptr;
			}
		}

3.イテレータ(ポインタと考えてください)

		iterator begin() {
			return _str;
		}

		iterator end() {
			return _str + _size;
		}

4.文字列クラスオブジェクトの容量操作

		size_t size()const {//有效元素个数
			return _size;
		}

		size_t capacity()const {//容量
			return _capacity;
		}

		bool empty()const {//判断有效元素个数是否为0
			if (size() == 0) {
				return true;
			}
			return false;
		}

		void resize(size_t n, char c = '\0') {//改变有效元素个数,以c填充
			int new_size = n;
			if (new_size > size()) {
				if (new_size > capacity()) {
					reserve(new_size);
				}
				int i;
				for (i = size(); i < new_size; i++) {
					_str[i] = c;
				}
			}
			_size = new_size;
			_str[n] = '\0';
		}

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

5.文字列クラスオブジェクトの変更操作

		void push_back(char c) {//尾插字符
			if (_size == _capacity) {
				reserve(_capacity*2);
			}
			*end() = c;
			_size++;
			*end() = '\0';
		}

		string& operator+=(char c) {//+=运算符重载,类似于尾插字符
			push_back(c);
			return *this;
		}

		void append(const char* str) {//在尾部追加字符串
			size_t new_size = _size + strlen(str);
			if (new_size > _capacity) {
				reserve(_capacity * 2);
			}
			int i = _size,j=0;
			while (str[j] != '\0') {
				_str[i] = str[j];
				++i, ++j;
			}
			_str[i] = '\0';
			_size = i;
		}

		string& operator+=(const char* str) {//+=运算符重载,类似于尾插字符串
			append(str);
			return *this;
		}

		void clear() {//将有效元素个数置为0
			*begin() = '\0';
			_size = 0;
		}

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

		const char* c_str()const {//将string对象转为字符串
			return _str;
		}

6.文字列クラスオブジェクトへのアクセス

		char& operator[](size_t index) {//[]运算符重载(普通类型)
			if (index<0 || index>=size()) {
				perror("operator[]:");
			}
			return *(_str + index);
		}

		const char& operator[](size_t index)const {//[]运算符重载(const类型)
			if (index < 0 || index >= size()) {
				perror("const operator[]:");
			}
			return *(_str + index);
		}

7.文字列クラスの非メンバー関数

		bool operator<(const string& s) {//<运算符重载
			if (strcmp(_str, s._str) < 0) {
				return true;
			}
			return false;
		}

		bool operator<=(const string& s) {//<=运算符重载
			if (strcmp(_str, s._str) > 0) {
				return false;
			}
			return true;
		}

		bool operator>(const string& s) {//>运算符重载
			if (strcmp(_str, s._str) > 0) {
				return true;
			}
			return false;
		}

		bool operator>=(const string& s) {//>=运算符重载
			if (strcmp(_str, s._str) < 0) {
				return false;
			}
			return true;
		}

		bool operator==(const string& s) {//==运算符重载
			if (strcmp(_str, s._str) == 0) {
				return true;
			}
			return false;
		}

		bool operator!=(const string& s) {//!=运算符重载
			if (strcmp(_str, s._str) == 0) {
				return false;
			}
			return true;
		}

8.文字列クラスの文字列に関連する特別な操作

// 返回c在string中第一次出现的位置

		size_t find(char c, size_t pos = 0) const {
			if (pos < 0 || pos >= size()) {
				perror("find c:");
			}
			int i;
			for (i = pos; i < size(); i++) {
				if (_str[i] == c) {
					return i;
				}
			}
			return -1;
		}

// 返回子串s在string中第一次出现的位置

		size_t find(const char* s, size_t pos = 0) const {
			if (pos < 0 || pos >= size()) {
				perror("find s:");
			}
			int i;
			for (i = pos; i < size(); i++) {
				int j = 0;
				int i_1 = i;
				while (i_1 < size() && j < strlen(s) && _str[i_1] == s[j]) {
					++i_1, ++j;
				}
				if (j == strlen(s)) {
					return i;
				}
			}
			return -1;
		}

// 在pos位置上插入字符c/字符串str,并返回该字符的位置

		string& insert(size_t pos, char c) {
			if (pos < 0 || pos >= size()) {
				perror("insert c:");
			}
			int new_size = size() + 1;
			if (new_size > capacity()) {
				reserve(capacity() * 2);
			}
			auto i = begin() + pos;
			for (auto j = end() - 1; j >= i; --j) {
				*(j + 1) = *j;
			}
			*i = c;
			_size = new_size;
			return *this;
		}

		string& insert(size_t pos, const char* str) {
			if (pos < 0 || pos >= size()) {
				perror("insert s:");
			}
			int new_size = size() + strlen(str);
			if (new_size > capacity()) {
				reserve(capacity() * 2);
			}
			auto i = begin() + pos;
			for (auto j = end() - 1; j >= i; --j) {
				*(j + strlen(str)) = *j;
			}
			for (int j = 0; str[j] != '\0'; ++j) {
				*i = str[j];
				++i;
			}
			_size = new_size;
			return *this;
		}
		
// 删除pos位置上的元素,并返回该元素的下一个位置

		string& erase(size_t pos, size_t len) {
			if (pos < 0 || pos >= size()) {
				perror("erase:");
			}
			auto i = begin() + pos;
			int new_size = size() - len;
			for (auto j = i + len; j < end(); ++j) {
				*i = *j;
				++i;
			}
			_size = new_size;
			return *this;
		}

9.文字列オブジェクトの入出力関数のオーバーロード

//输出函数重载
ostream& lzStr::operator<<(ostream& _cout, const lzStr::string& s) {
	int i;
	for (i = 0; i < s.size(); i++) {
		_cout << s[i];
	}
	return _cout;
}

//输入函数重载
istream& lzStr::operator>>(istream& _cin, lzStr::string& s) {
	char* str = (char*)malloc(sizeof(char) * 100);
	char* buf = str;
	int i = 1;
	while ((*buf = getchar()) == ' ' || (*buf == '\n'));
	for (; ; ++i) {
		if (*buf == '\n') {
			*buf = '\0';
			break;
		}
		else if (*buf == ' ') { 
			*buf = '\0';
			break;
		}
		else if (i % 100 == 0) { 
			i += 100; 
			str = (char*)realloc(str, i);
		}
		else {  
			buf = (str + i);
			*buf = getchar();
		}
	}
	s._str = str;
	s._capacity = s._size = i;
	return _cin;
}

5.文字列クラスを使用する際に注意が必要な事項

1.実際の文字列クラスの内部メンバー変数

文字列型のオブジェクトを作成するときは、sizeof()を直接使用してそのサイズを出力します。32ビットのオペレーティングシステムでは28バイトであることがわかりますが、推測する文字列クラスの変数は次のとおりです。char * str、size_t size、size_t capacity;これらの変数から計算される文字列のサイズは12である必要があるのに、なぜ16バイト余分にあるのですか?c ++が文字列コンテナを作成したとき、文字列の長さが書き込む文字列は16バイト未満であり、新しい動的文字配列に対してchar * strを使用しません。この設定により、文字列を使用する場合の効率が大幅に向上します。もちろん、文字列に対して逆を使用します。これらの問題は、オブジェクトの場合にも発生します。文字列オブジェクトの容量を増やし続けても問題はありませんが、逆に文字列オブジェクトの容量を減らしても、文字列オブジェクトの容量は変わりません。多くのプロセスを経るのは簡単ではないので、ライフサイクルが終了する前にスペースを解放する必要はありませんが、容量を16ワード未満に減らすと例外が発生します。セクションの時点で、 C++から提供されたstr[16]は完全に十分なので、動的アプリケーション用のスペースが解放され、str [16]を直接使用できます。現時点では、1ワードが必要なため、容量のサイズは15です。 。'\0'を格納するセクション、再度縮小しても変更されません。

2.実際の文字列クラス拡張メソッド(コンパイラが異なれば異なる場合があります)

文字列内の予約を使用して拡張する場合、コンパイラは要件に従って拡張しませんが、容量がニーズ以上になり停止するまで、容量* 1.5(vs)に従って拡張します。ベクトルは同じではありません。 、ベクトルで予約を使用して拡張する場合、コンパイラは必要に応じて拡張し、必要なだけ拡張しますが、小さいサイズに拡張しても効果はありません。

3.容量拡張のために予備を使用するプロセス

1.新しいスペースを開きます。   

2.要素をコピーします。   

3.古いスペースを解放します。   

4.新しいスペースを使用します。

4.[]とatの違い

[]とatを使用する場合は、両者の違いに注意する必要があります。どちらも文字列内の要素に同じ効果でアクセスできます。違いは、エラーが発生したときに[]を使用すると、プログラムは直接クラッシュします。エラーが発生した場合、atは例外をスローします。

5.コピーコンストラクタと代入演算子のオーバーロードに関する詳細

質問:

文字列クラスを自分で実装する場合、クラスコピーコンストラクタで浅いコピーを使用する問題と代入演算子のオーバーロードに注意する必要があります。

b。浅いコピーを解決するためのいくつかの方法:

①。ディープコピー。(旧)

(スペースリソースのスケジューリングはpremiseクラスに存在します)コピーコンストラクターと代入演算子のオーバーロードの2つのメンバー関数で、古いバージョンのディープコピーを使用して実装します。つまり、スペースリソースのコピーが含まれる場合は、毎回呼び出します。呼び出し元のオブジェクトが使用する新しいスペースを作成します。

②。ディープコピー。(新規)

(スペースリソースのスケジューリングはpremiseクラスに存在します)コピーコンストラクターの2つのメンバー関数と割り当て演算子のオーバーロードで新しいバージョンのディープコピーを使用して実装します。つまり、これら2つの関数で_strパラメータオブジェクトはパラメータとして呼び出されますクラスのコンストラクタはオブジェクトを再構築し、一時オブジェクトのメンバーを、これら2つの関数を1つずつ呼び出すオブジェクトのメンバーと交換します。最後に、呼び出すオブジェクトに新しいメンバーがあります。これらの2つの関数。一時オブジェクトのメンバーが解放されます。

③。コピーオンライト(欠陥にはお勧めしません)

コピーオンライトとは何ですか?

コピーコンストラクターと代入演算子のオーバーロードを定義するときは、もちろん、シャローコピーメソッドを使用して定義します。もちろん、疑問があるかもしれません。シャローコピーを使用して定義すると、これら2つのメンバー関数が呼び出されたときに、確実に定義されます。表示同じスペースを使用する複数のオブジェクトの問題、それを解決するにはどうすればよいですか?複数のオブジェクトに同じスペースを使用する場合、スペースの値を変更しない限り問題はないので、別のオブジェクトを追加しますスペースへのブロックスペースは、このスペースで共有されているオブジェクトの数を記録するために使用されます。コピーコンストラクターと代入演算子のオーバーロードでは、このスペースのカウンターは、呼び出されるたびに1ずつ増加し、浅いコピーが実行されます。 。メンバー関数では、このカウンタースペースの値が検出されます。共有数が1より大きい場合は、新しいスペースが適用され、元のスペースのコンテンツがコピーされ、新しいスペースのカウンターの値がコピーされます。スペースは1に割り当てられ、古いスペースのカウンターの値は-1です。オブジェクトがこの新しいスペースをポイントして操作できるようにします。これはコピーオンライトです。さらに、注意が必要です。オブジェクトが使用するスペースのカウンターが1であるかどうか、1でない場合、カウンターの値は-1になり、1の場合、スペースは-1になります。リリースされる。

また、読み取り時にコピーしますか?(欠陥)

[]またはイテレータを使用すると、スペース内のコンテンツを変更するか、スペース内のコンテンツにアクセスできるため、コンパイラは何をしたいかわからないため、これら2つのメンバー関数内に判断が追加されます。スペースにオブジェクトが1つしかない場合は、対応する値を直接返すことができますが、複数のオブジェクトが使用されている場合でも、コピーオンライトテクノロジが処理に使用されます。したがって、[]を使用してスペースの内容をトラバースすると、システムはこのスペースがオブジェクトによって使用されていないことがわかります。読み取り専用であっても、[]関数は続行されます->新しいスペースを再適用します- ->要素のコピー->新しいスペースを使用し、一連のコピーオンライト手法が処理を実行するのを待ちます。

6.string和vector<char>

質問:

文字列クラスは文字を管理する動的シーケンステーブルであり、ベクトルは任意のタイプの要素を管理する動的シーケンステーブルです。vector<char>は文字配列であるため、STLが個別の文字列クラスを提供するのはなぜですか。

違い:

1. c / c ++では、文字列の最後の有効な要素は\ 0である必要があると規定されているため、文字配列は必ずしも文字列である必要はありません。

2.文字列クラスには、文字列に関連するいくつかの特別な操作があります。

おすすめ

転載: blog.csdn.net/weixin_49312527/article/details/124106668