ユーザーからいくつかの値を読み取り、特定のサイズの配列を作成する必要がある場合、次のようにします。
#include <iostream>
using namespace std;
unsigned* numbers;
int main()
{
int a;
cin >> a;
numbers = new unsigned[a];
}
2次元配列(ユーザーから読み取ったサイズのa * b)でどうすればよいですか?
ユーザーからいくつかの値を読み取り、特定のサイズの配列を作成する必要がある場合、次のようにします。
#include <iostream>
using namespace std;
unsigned* numbers;
int main()
{
int a;
cin >> a;
numbers = new unsigned[a];
}
2次元配列(ユーザーから読み取ったサイズのa * b)でどうすればよいですか?
a と b がそれぞれ行数と列数の場合、次のように配列を割り当てることができます。
new unsigned[a * b];
i
行と列の要素にアクセスするには、次のj
ようにします。
numbers[i * b + j]
ただし、実際にstd::vector
は、やろうとしていることは何でも使用する方がほぼ確実に優れていることに注意してください。ただし、まだそれについて学んでいない可能性があります:)
コード内で 2D 配列のように見えるものはすべて、メモリ内の物理的な 2D 配列ではなく、割り当て方法に応じて、メモリの 1 つのプレーン ブロックまたは散らばった配列になります。
Brennan Vincent の答えは、a*b 要素を含む 1 つの動的配列の割り当てを提案しています。これにより、メモリ内に 1 つの連続したブロックが得られます。それを彼が言及した組み込みの動的メモリ管理と組み合わせて、std::vector
満足してください。
std::vector<unsigned> matrix(a*b);
マトリックスにアクセスしやすくしたい場合は、すべてをクラスにラップして、2D 座標で要素にアクセスできるようにします。ただし、自分でメモリを管理することから離れてください。それは、あなたとそのコードを維持しなければならない (そしてメモリ リークを探す必要がある) 人を傷つけるだけです。
ブーストがすでにそれを行ったことは注目に値します。したがって、クラスにカプセル化することを意図していない場合は、それに固執します。
一般に、生の配列と割り当てを使用することは、を使用することよりも劣りstd::vector
ます。もちろん、を使用することもできますが、vector<vector<T>>
入力に時間がかかるだけでなく、不要なコピーが必要になる場合もあります。
ptr_vector
またはを使用vector<unique_ptr<vector<int>>
すると2番目の問題は解決しますが、問題はさらに複雑になります。それで、それをどのように解決するのですか?
簡単です。2D配列はまったく使用しないでください。
メモリ内の2D配列を想像してみてください。
[X|X|X|X]
[Y|Y|Y|Y]
[Z|Z|Z|Z]
それを一列に並べることができることは十分に明らかです:
[X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z]
vector
これで、古くて馴染みのある1D配列に戻ります(またはCスタイルの配列であるかどうかは関係ありません)。要素に到達するには、最初の配列の幅を知って、n番目の行に対応する配列要素に移動し、列の数を単純に追加する必要があります。
int& access (unsigned x, unsigned y) {
return data[x * sizeY + y]; // Or (y * sizeX + x)
}
スタブ方式なので、実際の使用に問題があるかもしれません。グローバル実装の例を次に示します。
int& accessVectorAs2d (std::vector<int> & data, unsigned x, unsigned y, unsigned int sizeY); // inside stays the same
unsigned SizeX = 20, SizeY = 20;
std::vector<int> V (SizeX * SizeY, 0);
accessVectorAs2d(V, 1, 3, SizeX) = 5;
通常の2D配列と非常によく似た方法で1D構造を使用できるようにするために、参照が返されます。
// So you can do either reads
int X = access(1, 3);
// Or writes!
access(1,3) = 5;
追記:独自のクラスを作成する場合、オーバーロードoperator()
するとさらに良い結果が得られます。
Data(1, 3) = 5;
int X = Data(1, 3);
ここで、そのアクセスをどのように実装するか(ベクトルをサブクラス化またはカプセル化するか、単純なグローバル関数を使用するか)は実際には重要ではありません。
ここで使用することを強くお勧めしますstd::vector
。これにより、メモリがリークする(削除を忘れる)ことがなくなり、サイズを(.push_back()
または.reserve()
で)変更するのが簡単になり、一般的に推奨される方法です。結局のところ、CではなくC++で書いているのです。
実際には、クラスはもちろん、タイプだけでなく、次元の数(わかりました、簡略化できます)と次元のサイズ(-1つごと)(片側で無制限に拡張できるように-1)を使用してテンプレート化する必要があります。これはおそらく、コンパイル時に適切なアクセス関数を作成し、計算することによって最もよく行われます。
Sum i = 0 i <n(サイズi)i * D i、
ここで、nは次元の数、Sは次元サイズの配列、Dは座標の配列です。
1 つのベクトルを使用して、2 次元配列を表すことができます。
typedef std::vector<int> d1_type; // 1D
typedef std::vector<d1_type> d2_type; // 2D
typedef std::vector<d2_type> d3_type; // 3D
int num = 5;
d2_type value2d(num, d1_type(num));
d3_type value3d(num, d2_type(num, d1_type(num)));
また、配列のような 2D/3D ベクトルにアクセスできます。次に例を示します。
value2d[0][0] = 100;
value3d[0][0][0] = 100;
ベクトルを使用してみてください。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// An empty vector of vectors. The space
// between the 2 '>' signs is necessary
vector<vector<int> > v2d;
// If you intend creating many vectors
// of vectors, the following's tidier
typedef vector<vector<int> > IntMatrix;
IntMatrix m;
// Now we'll try to create a 3 by 5 "matrix".
// First, create a vector with 5 elements
vector<int> v2(5, 99);
// Now create a vector of 3 elements.
// Each element is a copy of v2
vector<vector<int> > v2d2(3,v2);
// Print out the elements
for(int i=0;i<v2d2.size(); i++) {
for (int j=0;j<v2d2[i].size(); j++)
cout << v2d2[i][j] << " ";
cout << endl;
}
}
次の方法でc++new[]を使用してメモリを割り当てることができます。
int** array = new int*[x];
for(int i = 0; i < x; i++)
aryay[i] = new int[y];
これの単純な概念は、配列を指しているポインタの配列です。メモリリークを回避するためにも削除する必要があります。
「ベクトルのベクトル」ソリューションは満足のいくものですが、それらは 2D 構造ではありません。各「行」は互いに独立して割り当てられ、長さは生ごとに異なる可能性があります (したがって、2D 制約は手動で維持する必要があります: 挿入上下にある要素が相対的な位置を維持できるように、アイテムはすべての行を拡大する必要があります。
適切な動的 2D 構造が必要な場合は、ベクトル (単純な 1 次元) をクラス (" table
" と呼びましょう) でラップし、そのクラスに、外部表現された 2D として適切に維持するために必要な操作を提供できます。特に:
Colums() const
します。Rows() const
int& operator()(unsigned row, unsigned col) { return vect[row*Colums()+col]; }
aとその対応する constを定義するstruct coord { unsigned r, unsigned c }
aと便利な a を定義するtable::operator[](const coord&)
insert
します。remove
Colums()
r*Columns()
Columns()+1
c
end()
それらの値を一貫して定義するように注意してください) 。これらすべての重要なポイントはn = r*C+c
、2 番目のポイントで式を理解することです。他のすべては、ほとんど即時の結果にすぎません。
unsigned **numbers;
numbers = new unsigned*[a];
for (int i=0; i<a; i++){
numbers[i] = new unsigned[b];
}
メモリ内で連続していないという点で、2次元配列とまったく同じようには動作しませんが、動作します。表記numbers[i][j]
を使用して要素にアクセスすることもできます。
-ing自体の前に、最後にdelete[]
の各要素を覚えておいてくださいnumbers
delete[]
numbers
std::vector
他の投稿で詳しく説明されているように、使用はおそらく好ましい解決策です