vector const_iterator

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ゴジラストアおめでとう!

vector const_iterator

#1

投稿記事 by ゴジラストアおめでとう! » 1年前

vectorで作ったものにピンポイントでアクセスするためにconst_iteratorを使用するようにしたところ、今まで使っていたpush_backなどが消滅してoperatorが出てきました。

使い方があまりわかっておらず、参考になるサイトもなかなか見つからず質問させていただきました。

ここでいう元のvectorに続く機能?(例えば.begin()など)とoperatorの互換を教えてください。(互換できるのかな?そこらへんもよくわかっていません…)

例:
.begin() = operatorでいう~

.push_back() = operatorでいう~
といった感じで書いていただけるとわかりやすくて助かります。

ゴジラストアおめでとう!

Re: vector const_iterator

#2

投稿記事 by ゴジラストアおめでとう! » 1年前

見直したらpush_backも普通に使っているようなのですが、
std::vector<HOGE> hogeを std::vector<HOGE>::const_iterator hogeにしたところ、push_backしていたところでエラーを吐きました。(HOGEは構造体です)

エラー C2664 'const SETTING_TILE_DATA &std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<_Ty>>>::operator [](int) const': 引数 1 を 'SETTING_TILE_DATA' から 'int' へ変換できません

というエラーです。

const_iteratorを使う前まではなかったものです

maru
記事: 150
登録日時: 8年前

Re: vector const_iterator

#3

投稿記事 by maru » 1年前

std::vector<> は配列と同様なもの。ただし要素の追加/削除が可能。
要素を追加するためのメソッドの一つが push_back() 関数。

std::vector<>::const_iterator はその配列の要素をアクセスするための反復子の一つ。
反復子を取得するためのメソッドの一つが begin() 関数。
反復子を使用することにより配列の要素を順次アクセスすることができる。反復子を使用して要素をアクセスする時はポインタと同様に*を使用する。
iterator は要素を変更することができるが、 const_iterator は要素を変更することができない。

つまり、vector と vector::const_iterator は別物なので、置き換えはできない。ということです。

以下に反復子の使用例を示します。なお、この例では const_iteraor の取得として begin() ではなく cbegin() を使用します。

コード:

#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
	vector<int>	sample;
	for (int i = 0; i < 10; ++i)	sample.push_back(i);	// 要素の順次追加

	cout << "インデックスによるアクセス" << endl;
	for (int i = 0; i < 10; ++i)	cout << sample[i];
	cout << endl;

	cout << "ポインタによるアクセス" << endl;
	int* p = &sample[0];
	for (int* p = &sample[0]; p <= &sample[9]; ++p)	cout << *p; // for の終了条件に注意
	cout << endl;

	cout << "反復子によるアクセス" << endl;
	for (vector<int>::const_iterator cit = sample.cbegin(); cit != sample.cend(); ++cit)
		cout << *cit;
	cout << endl;
	// begin/endの別の使用法
	for (vector<int>::const_iterator cit = cbegin(sample); cit != cend(sample); ++cit)
		cout << *cit;
	cout << endl;

	cout << "逆反復子によるアクセス" << endl;
	for (vector<int>::const_reverse_iterator crit = sample.crbegin(); crit != sample.crend(); ++crit)
			cout << *crit;
	cout << endl;
	return 0;
}

ゴジラストアおめでとう!

Re: vector const_iterator

#4

投稿記事 by ゴジラストアおめでとう! » 1年前

変数名をそのままにしてconst_iteratorをつけても赤線が出ないもので、似たような機能なのかと勘違いしていました。

違いも機能の働きもわかったのですが、for文で回しているところの要素をピンポイントでとってきたい場合にはどうすればよいのでしょうか?

今やっているのは、vectorで構造体の配列を作っていて、その構造体の中身を取ってくるというものです。

sampleとcitの関係と代入(?)の動きが今一つ追いかねてます。

身の丈に合わないことをやっているのは重々承知なのですが、時間がないので、この方法に拘りたく考えています。

よろしければ回答のほどどうぞよろしくお願いいたします

maru
記事: 150
登録日時: 8年前

Re: vector const_iterator

#5

投稿記事 by maru » 1年前

「要素をピンポイントでとってきたい」というのが私には意味不明なんですが...
vector の特定の順番(x番目)の要素をとってきたいのなら添字(インデックス [])を使って

コード:

HOGE a = hoge[x];
とすればよいだけで反復子を持ち出す必要はない。
反復子を使って同じことをやろうとしたら

コード:

vector<HOGE>::const_iterator cit = cbigen(hoge);// 先頭要素の反復子を取得
advance(cit, x);// 反復子をx番目まで進める
HOGE a = *cit;// x番目の要素を配列からコピー
インデックスを使うより手間がかかります。

そもそも反復子を使用する目的は、配列やリストなどのコンテナと検索などのアルゴリズムを分離することです。
つまり、配列で作っておいたプログラムのアルゴリズムを簡単にリスト構造やほかのデータ構造でも適用できるようにするためです。

もしかして find() 系の関数を使って反復子が返ってきているのでそれを使う必要があるってこと?
でも find() の戻り値は const_ がついてなかったような気がする。それに、find() が使える人が反復子の使い方を知らないとは思えないし。
オフトピック
老婆心ながら言っておくと、vector に push_back() を使わないほうが幸せになれるかも。事前 reserve() でメモリを確保しておけば別ですが。
(というか、自分は基本的に vector とわかっているコンテナに push_back()を使うことはない。 )

ゴジラストアおめでとう!

Re: vector const_iterator

#6

投稿記事 by ゴジラストアおめでとう! » 1年前

私のイメージに合うものがあったので貼ります

コード:

struct foo
{
        int     x;
        int     y;
        int     z;
};
int main (int argc, char **argv)
{
 vector<foo> v;
        foo a = { 9, 8, 7}, b = { 3, 2, 3}, c = { -1, -2, -3};
 
        v.push_back (a);
        v.push_back (b);
        v.push_back (c);
 
        for (std::vector<foo>::const_iterator i = v.begin ();
                        i != v.end (); i++)
        {
                (void) printf ("%d\n", i->x);
        }
vectorのx番目ではなく、vectorのx番目に入っている、複数あるものの1つを取ってくる。ということです。
普通にvectorを使っていると私の調べた限りではできなくて、このやり方に行き着きました。

ごめんなさい、わかっている人には伝わりにくい言い回しでした。
複数ある要素の中から1つだけを取ってくるというのは、使いこなせないものからすると、ピンポイントで取ってくるという表現がしっくり来たもので…><

もしかしてとありましたが、find()は使ってないです。
Offtopic :のことですが、そうなのであれば何故そうなのかと、他の代替できるものを調べますが、現状動きすらしないので一先ずは知っている範囲のことで、動くものを提供させていただきます。



私の語彙力などが原因で混乱を招くやもしれぬと警戒して、例を出して質問させていただいていましたが、親切にお答えくださるので、何がしたくてこの機能を使おうとしているのかを具体的に書かせていただきます。

ゲームを制作中でして、もろもろのステータスをcsvファイルで読み込み、それをvectorで配列としています。(vectorには構造体を入れてます)
Dxライブラリで制作しているのですが、画像表示のために画像ファイルのパスが必要で、それを取って来たくてこの方法を使用しようと考えています

コード:

DrawGraph(x, y, class.vector->graphic_handle, TRUE); //とあるクラス作成されたベクターの中にある画像パス
まだ初心者でこのやり方も最適とはわからない状態でやっています。
わからずに迷走した挙句この方法を取っているだけであって、この方法でやることにこだわりはないです。
これに代わる初心者でも簡単にわかるやり方があれば、もちろんそれに越したことはないのですが…

長文になってしまいましたが、よろしければ回答の程どうぞよろしくお願いいたします。

ゴジラストアおめでとう!

Re: vector const_iterator

#7

投稿記事 by ゴジラストアおめでとう! » 1年前

いろいろいじくって自己解決しました
本当にありがとうございました

maru
記事: 150
登録日時: 8年前

Re: vector const_iterator

#8

投稿記事 by maru » 1年前

構造体の要素をメンバ変数といいます。
構造体のメンバは「構造体変数名.メンバ変数名」、「構造体変数名へのポインタ->メンバ変数名」でアクセスできます。
反復子を使用する場合はポインタと同様です(「構造体コンテナの反復子->メンバ変数名」、「(*構造体コンテナの反復子).メンバ変数名」。)

コード:

struct foo
{
	int	x;	// 構造体メンバx
	int	y;	// 構造体メンバy
	int	z;	// 構造体メンバz
};
int main(int argc, char *argv[])
{
	struct foo v[] = {{ 9, 8, 7 },{ 3, 2, 3 }, { -1, -2, -3 } };
	const size_t	vsize = sizeof(v) / sizeof(foo);
	vector<struct foo>	vec(vsize);

	// 配列添字によるアクセス
	for (int i = 0;	i < vsize; ++i)
	{
		vec[i] = v[i];	// vector要素の代入
		printf("%d, %d, %d\n", v[i].x, v[i].y, v[i].z);
	}
	// 配列ポインタによるアクセス
	for (struct foo* p = v; p != v + vsize; ++p)
	{
		printf("%d, %d, %d\n", p->x, p->y, p->z);
	}
	// vector添字によるアクセス
	for (int i = 0; i < vsize; ++i)
	{
		printf("%d, %d, %d\n", vec[i].x, vec[i].y, vec[i].z);
	}
	// vectorポインタによるアクセス
	for (struct foo* p = &vec[0]; p != &vec[0] + vsize; ++p)
	{
		printf("%d, %d, %d\n", p->x, p->y, p->z);
	}
	// vectorイテレータによるアクセス
	for (vector<struct foo>::const_iterator i = cbegin(vec); i != cend(vec); ++i)
	{
		printf("%d, %d, %d\n", i->x, i->y, i->z);
	}

	return 0;
}
オフトピック
push_back() は要素追加なので当然配列サイズが増える。要素が増えるとき配列の予備領域が不足すると「配列全体を再確保/データのコピー/旧領域の解放」する必要があり、この分のコストが発生する。
配列サイズが小さいうちはこのコストは無視可能であろうが、大きくなると無視できなくなる。
配列サイスが予め分かっているならば、その分のサイズで作成しておき、順次代入する。または reserve()関数により予備領域を確保したうえでpush_back()する。という方法がある。
配列サイスが予め分からなければ、一旦データを数えてから配列を作成したほうが良い場合もある。
順次アクセスしか行わないのであれば、listにする選択肢もある。

コード:

#include <list>
int main(int argc, char *argv[])
{
	struct foo v[] = { { 9, 8, 7 },{ 3, 2, 3 },{ -1, -2, -3 } };
	list<struct foo>	l;

	// listへの要素追加
	for (int i = 0; i < sizeof(v) / sizeof(foo); ++i)
	{
		l.push_back(v[i]);	// vector要素の代入
	}
	// イテレータによるアクセス
	for (list<struct foo>::const_iterator i = cbegin(l); i != cend(l); ++i)
	{
		printf("%d, %d, %d\n", i->x, i->y, i->z);
	}

	return 0;
}

返信

“C言語何でも質問掲示板” へ戻る