C++ 注記 - 5 番目のベクトル、配列に似ていますが、配列とは異なります

目次

1.ベクターの導入と利用

1ベクトルの概要 

2.ベクターの使用 

2.1 ベクトルの定義

2.2 ベクトル反復子の使用

2.3 ベクトル空間の成長問題

2.4 ベクターの追加、削除、確認、変更 

2.5 ベクトル反復子の無効化問題 

2、ベクトル深度分析

                1. memcpy を使用して問題をコピーします 

2.動的二次元配列




1. ベクターの導入と利用



1 ベクトルの概要


 1. ベクトルは、可変サイズの配列を表すシーケンス コンテナーです。


2. 配列と同様に、vector も要素を格納するために連続記憶領域を使用します。つまり、配列と同じくらい効率的に添字を使用してベクトルの要素にアクセスできるということです。ただし、配列とは異なり、そのサイズは動的に変更でき、そのサイズはコンテナによって自動的に処理されます。


3. 基本的に、vector は動的に割り当てられた配列を使用して要素を格納します。新しい要素が挿入されると、記憶領域を増やすために配列のサイズを変更する必要があります。これは、新しい配列を割り当て、すべての要素をこの配列に移動することによって行われます。新しい要素がコンテナに追加されるたびにベクトルのサイズが変更されないため、これは時間の点で比較的高価なタスクです。


4. ベクトル スペース割り当て戦略: ストレージ スペースが実際に必要なストレージ スペースよりも大きいため、ベクトルは、増加の可能性を考慮して追加のスペースを割り当てます。ライブラリーが異なれば、スペースの使用量と再割り当てを比較検討するための異なる戦略が採用されます。ただし、いずれの場合も、最後に要素を挿入する処理が一定の時間計算量で行われるように、再割り当ての間隔サイズは対数的に増加する必要があります。


5. したがって、Vector は、ストレージ スペースを管理し、効率的な方法で動的に拡張する機能を得るために、より多くのストレージ スペースを占有します。


6. 他の動的シーケンス コンテナ (deque、list、forward_list) と比較して、vector は要素にアクセスする際の効率が高く、最後の要素の追加と削除も比較的効率的です。最後ではない他の削除および挿入操作の場合、効率は低くなります。リストと forward_list の統合イテレータと参照よりも優れています。


2. ベクターの使用


2.1 ベクトルの定義

コンストラクター宣言 インターフェースの説明
ベクター() パラメータ構築なし
ベクトル(size_type n, const value_type& val = value_type()) n 個の値を構築して初期化する
ベクトル (const ベクトル& x);  コピー構築
ベクトル (InputIterator が最初、InputIterator が最後);  イテレータを使用して構築を初期化する

2.2 ベクトル反復子の使用

イテレータの使用 インターフェースの説明
begin()+end()

最初のデータ位置の iterator/const_iterator を取得します。

Iterator/const_iterator で最後のデータの次の位置を取得します。

rbegin()+rend()

最後のデータ位置の reverse_iterator を取得します。

最初のデータの前の位置の reverse_iterator を取得します

void PrintVector(const vector<int>& v)
{
	// const对象使用const迭代器进行遍历打印
	vector<int>::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}



vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

vector<int>::iterator it = v.begin();
while (it != v.end())
{
	cout << *it << " ";
	++it;
}
cout << endl;

// 使用迭代器进行修改
it = v.begin();
while (it != v.end())
{
	*it *= 2;
	++it;
}

// 使用反向迭代器进行遍历再打印
vector<int>::reverse_iterator rit = v.rbegin();
while (rit != v.rend())
{
	cout << *rit << " ";
	++rit;
}
cout << endl;

2.3 ベクトル空間の成長問題

容量スペース インターフェースの説明
サイズ データ数を取得する
容量  容量のサイズを取得する
空かどうかを判断する
サイズ変更 ベクトルのサイズを変更する
予約 ベクトルを容量に変換

1. 容量コードを vs および g++ でそれぞれ実行すると、vs での容量は 1.5 倍、g++ では 2 倍に増加することがわかります。vs は PJ バージョン STL、g++ は SGI バージョン STL です。


2. リザーブはスペースを開くことのみを担当します。必要なスペースがどのくらいかわかっている場合、リザーブはベクトル展開のコスト上の欠点を軽減できます。


3. スペースを開くときにサイズ変更も初期化され、サイズに影響します 

4.サイズ変更: 要素の数のみを変更します。要素の数がスペースのサイズを超える場合、
   容量予約を拡張します: スペースを減らさずに容量を拡張します。

2.4 ベクターの追加、削除、確認、変更 

ベクトルの追加、削除、確認、変更 インターフェースの説明
プッシュバック テールプラグ
ポップバック 末尾削除
探す 見上げる
入れる  位置の前に val を挿入します
消す その位置のデータを削除します
スワップ  2 つのベクトルのデータ空間を交換する
オペレーター[]  配列のようにアクセスする

以下のコードを実行して結果を確認してください。

ポップバック、プッシュバック

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	int a[] = { 1, 2, 3, 4 };
	vector<int> v(a, a + sizeof(a) / sizeof(int));
	vector<int>::iterator it = v.begin();
	while (it != v.end()) {
		cout << *it << " ";
		++it;
	}
	cout << endl;

	v.pop_back();
	v.pop_back();

    v.push_back(666);

	it = v.begin();
	while (it != v.end()) {
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

 検索、挿入、消去

// find / insert / erase
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
	int a[] = { 1, 2, 3, 4 };
	vector<int> v(a, a + sizeof(a) / sizeof(int));
	// 使用find查找3所在位置的iterator
	vector<int>::iterator pos = find(v.begin(), v.end(), 3);

	// 在pos位置之前插入30
	v.insert(pos, 30);

	vector<int>::iterator it = v.begin();
	while (it != v.end()) {
		cout << *it << " ";
		++it;
	}
	cout << endl;
	pos = find(v.begin(), v.end(), 3);

	// 删除pos位置的数据
	v.erase(pos);

	it = v.begin();
	while (it != v.end()) {
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

さまざまなトラバース方法

// operator[]+index 和 C++11中vector的新式for+auto的遍历
// vector使用这两种遍历方式是比较便捷的。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
	int a[] = { 1, 2, 3, 4 };
	vector<int> v(a, a + sizeof(a) / sizeof(int));

	// 通过[]读写第0个位置。
	v[0] = 10;
	cout << v[0] << endl;

	// 通过[i]的方式遍历vector
	for (size_t i = 0; i < v.size(); ++i)
		cout << v[i] << " ";
	cout << endl;

	vector<int> swapv;
	swapv.swap(v);
	cout << "v data:";
	for (size_t i = 0; i < v.size(); ++i)
		cout << v[i] << " ";
	cout << endl;
	cout << "swapv data:";
	for (size_t i = 0; i < swapv.size(); ++i)
		cout << swapv[i] << " ";
	cout << endl;

	// C++11支持的新式范围for遍历
	for (auto x : v)
		cout << x << " ";
	cout << endl;
	return 0;
}

2.5 ベクトル反復子の無効化問題 

イテレータの主な機能は、アルゴリズムが基礎となるデータ構造を考慮しないようにすることです。基礎となるデータ構造は実際にはポインタであるか、ポインタをカプセル化します。たとえば、vector のイテレータは元のエコロジー ポインタ T*ですしたがって、イテレータの無効化は実際には、イテレータの下部にある対応するポインタが指すスペースが破壊され、解放されたスペースの一部が使用されることを意味し、その結果、プログラムがクラッシュします (つまり、無効なイテレータを使用し続けると、プログラムがクラッシュする可能性があります。)。

(1)基礎となる空間に変更を引き起こす操作により、イテレータが失敗する可能性があります。たとえば、sizereserveinsertassign (ベクトルを再割り当てすると、基礎となる容量が変更される可能性があります v.assign(100, 8); )。プッシュバックなど

解決策: 上記の操作が完了した後、反復子を介してベクトル内の要素を操作し続ける場合は、反復子 (ここではそれ) を再割り当てするだけで済みます。

while(it != v.end())
{
    cout<< *it << " " ;
    ++it;
}

(2)指定位置の要素の削除操作--erase

Erase が位置 pos の要素を削除した後、位置 pos の後の要素は、基になる空間に変更を引き起こすことなく前方に移動します。理論的には、イテレータは無効化されるべきではありませんが、削除後に pos がたまたま最後の要素だった場合、posたまたま終了位置であり、終了位置に要素がない場合、 pos は無効になりますしたがって、ベクトル内の任意の位置にある要素を削除すると、 vs はその位置にある反復子が無効であるとみなします。 

解決策: イテレータを使用する前に再割り当てしてください。



2、ベクトル深度分析



1. memcpy を使用して問題をコピーします 


1. memcpy はメモリのバイナリ形式のコピーで、あるメモリ空間の内容を別のメモリ空間にそのままコピーします。
2. カスタム タイプの要素をコピーする場合、memcpy は効率的でエラーはありませんが、カスタム タイプの要素をコピーし、そのカスタム タイプの要素にリソース管理が関与している場合は、エラーが発生します。 memcpy のコピー これは実際には浅いコピーです。


3. オブジェクトにリソース管理が含まれる場合、memcpyはシャロー コピーであるため、オブジェクト間のコピーに memcpy を使用しないでください。そうしないと、メモリ リークやプログラムのクラッシュが発生する可能性があります。

浅いコピーと深いコピーを覚えていますか? 覚えていない場合は、文字列の記事にアクセスして見てください。

以下に簡単に言及します。

浅いコピー: 2 つのポインタ p1 と p2 は同じ空間を指しており、p2 が解放されると 2 回削除されます。コピー後、p1 の内容を変更できます。

ディープ コピー: p2 は新しいスペースを開き、p1 のコンテンツを p2 にコピーします。コピー後、p1 の元のコンテンツは変更されません。


2. 動的二次元配列


vec.size()=行
vec[0].size()=列


(写真はネットから拾ってきました) 

おすすめ

転載: blog.csdn.net/MuqiuWhite/article/details/129666909