(C++)std::vectorのresize()による初期化について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
Ketty
記事: 102
登録日時: 10年前

(C++)std::vectorのresize()による初期化について

#1

投稿記事 by Ketty » 9年前

こんにちは(^^) Kettyです。

C++(stl)の、std::vectorのresize()について質問です。

前提として、引数ありのコンストラクタだけがpublicになっているクラス(CTestとします)があり、
別クラス(Mainとします)で、CTestのインスタンスを複数格納するためのvectorを用意したとして、
このvectorをresize()で初期化するとした場合、どのようにコーディングするのがよいのでしょうか?
CTestのインスタンスのコピーを作らずにやりたいのですが、それは無理なのでしょうか。

以下に具体的なコードを提示いたします。
コメントアウトしている部分が質問の箇所となります。
何か方法がありましたら、ご教示ください。

コード:

#include <iostream>
#include <vector>
#include <string>

//++++++++++++++++++++++
// 任意のクラス
//++++++++++++++++++++++
class CTest {
public:
	// 引数つきコンストラクタ(m_paramを初期化します)
	CTest( const std::string& param ):m_param( param ) {
		std::cout << "Constructor...!" << std::endl;
	}

	// デストラクタ
	~CTest( void ) {
		std::cout << "Destructor...!" << std::endl ;
	}

	// 出力するだけのメソッド
	void PrintParam() {
		std::cout << m_param << std::endl ;
	}

	// =演算子
	CTest& operator=( const CTest& r ) {
		return *this ;
	}
private:
	// 引数なしコンストラクタは非公開
	CTest( void ) ;

	// コンストです
	const std::string m_param ;
};

//++++++++++++++++++++++
// メイン
//++++++++++++++++++++++
int main() {

	// クラスの配列を空で用意
	std::vector<CTest> vec ;	// vector<CTest*>ではない

	// リサイズ数
	const int reNum = 2 ;

	// リサイズして初期化したいがparamを指定する方法が分からない(><)
	//vec.resize(reNum) ;					// ←これはparamを指定してないのでコンパイルできない
	//vec.resize(reNum, "aiueo") ;			// ←これはダメ。コンパイルできない
	//vec.resize(reNum, CTest("aiueo")) ; 	// ←これはコピーを作ることになるのでコンストラクタとデストラクタが呼ばれる

	// お試しで表示してみる
	for( int i=0, n=vec.size(); i<n; ++i ){
		vec[i].PrintParam() ;
	}

	// 終わります
	std::cout << "finish!" << std::endl ;
	std::cin.ignore() ;

	return 0 ;
}
環境:Visual Studio 2010 Express Edition
Windows 7 64bit

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: (C++)std::vectorのresize()による初期化について

#2

投稿記事 by h2so5 » 9年前

コピーしないとしてもコンストラクタを呼ばないと初期化できないと思うのですが。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: (C++)std::vectorのresize()による初期化について

#3

投稿記事 by h2so5 » 9年前

例えば以下のように書けばインスタンスのコピーは発生しませんが、代わりに初期化のためにコンストラクタが何度も呼ばれることになります。

コード:

vec.reserve(reNum);
auto len = vec.capacity() - vec.size();
for( int i=0; i<len; ++i ) {
    vec.emplace_back("aiueo");
}

アバター
Ketty
記事: 102
登録日時: 10年前

Re: (C++)std::vectorのresize()による初期化について

#4

投稿記事 by Ketty » 9年前

>h2so5さん
お世話になっております(^^)

すみません。↓の部分についておっしゃられているのだと思いますが私の説明が不十分でしたm(__)m

コード:

	//vec.resize(reNum, CTest("aiueo")) ;   // ←これはコピーを作ることになるのでコンストラクタとデストラクタが呼ばれる
上記のコードで説明させていただきますと、
私の認識では、
(1)CTestのコンストラクタが呼ばれてインスタンスが生成される
(2)resizeで、そのコピーが格納される
(3)(1)のデストラクタが呼ばれる
(4)main関数の終わりとともに、(2)のデストラクタが呼ばれる
という流れになると認識しておりますが、

これを、
(1)CTestのコンストラクタが呼ばれてインスタンスが生成される
(2)resizeで、そのインスタンスが格納される
(3)main関数の終わりとともに、(2)のデストラクタが呼ばれる

のようにする方法があるのかないのかをうかがいたく、
コンストラクタが呼ばれることが困るのではないですm(__)m

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: (C++)std::vectorのresize()による初期化について

#5

投稿記事 by h2so5 » 9年前

Ketty さんが書きました: (1)CTestのコンストラクタが呼ばれてインスタンスが生成される
(2)resizeで、そのインスタンスが格納される
(3)main関数の終わりとともに、(2)のデストラクタが呼ばれる
サイズの増加が2以上の場合どうするのでしょうか。

Blue

Re: (C++)std::vectorのresize()による初期化について

#6

投稿記事 by Blue » 9年前

resizeではなく、generate_nとback_inserterを使うとか。

テケトーコード

コード:

#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <iostream>

class Hoge
{
public:
	Hoge(const char* s) : s_(s) {
		countup();
		showstr("Hoge(const char*)");
	}
private:
	std::string s_;
	Hoge() {
		countup();
		showstr("Hoge()");
	}

	static int i_;
	void countup() { ++i_; }
	void showstr(const char* s) { std::cout << s << "(" << i_ << ")" << std::endl; }
};

struct HogeGenerater {
	const char* operator ()() {
		return "hoge";
	}
};

int Hoge::i_ = 0;

int main(void)
{
	std::vector<Hoge> v;

	std::generate_n(std::back_inserter(v), 3, HogeGenerater());
	//std::generate_n(std::back_inserter(v), 3, []() { return "hoge"; });

	return 0;
}

Blue

Re: (C++)std::vectorのresize()による初期化について

#7

投稿記事 by Blue » 9年前

あれ?テケトーコードで

コード:

v.resize(3, "hoge");
ってやってもコンストラクタが1回しか呼ばれないんだけどなんで?

Blue

Re: (C++)std::vectorのresize()による初期化について

#8

投稿記事 by Blue » 9年前

連投スマソ。

デストラクタに表示を追加したら、generate_nもコピーを作ってましたわ。
で、コピーコンストラクタが呼ばれていたわけだった。。。

コード:

#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <iostream>

class Hoge
{
public:
	Hoge(const char* s) : s_(s) {
		countup();
		showstr("Hoge(const char*)");
	}
	Hoge(const Hoge& h) {
		countup();
		s_ = h.s_;
		showstr("Hoge(const Hoge& h)");
	}
	~Hoge() {
		showstr("~Hoge()");
		countdown();
	}
private:
	std::string s_;
	Hoge() {
		countup();
		showstr("Hoge()");
	}

	static int i_;
	void countup() { ++i_; }
	void countdown() { --i_; }
	void showstr(const char* s) { std::cout << s << "(" << i_ << ")" << std::endl; }
};

int Hoge::i_ = 0;


int main(void)
{
	std::vector<Hoge> v;

	//v.resize(3, "hoge");
	std::generate_n(std::back_inserter(v), 3, []() { return "hoge"; });

	return 0;
}

アバター
Ketty
記事: 102
登録日時: 10年前

Re: (C++)std::vectorのresize()による初期化について

#9

投稿記事 by Ketty » 9年前

>h2so5さん
ありがとうございます。
No.3の書き込みを読み落としていました<(__)>

なるほど・・・、emplace_backというものがあるのですね。
知りませんでした。これで解決です(^^)

ご提示いただいたコードは、
reserveで領域を拡張させて、実サイズとの差分だけ末尾に追加するけど、
emplace_backなのでインスタンスのコピーを発生させない・・・ということですね。
h2so5 さんが書きました: サイズの増加が2以上の場合どうするのでしょうか。
つまり、emplace_backしたとしても、
emplace_backで追記した回数分、コンストラクタとデストラクタは呼ばれるけどいいの?、ということでしょうか(??)
でしたらよいです(^^)

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: (C++)std::vectorのresize()による初期化について

#10

投稿記事 by h2so5 » 9年前

すみません、先ほど投稿したコードはreserveに渡した引数とcapacityが一致する保証がないので間違いです。
正しくは、

コード:

vec.reserve(reNum);
auto len = reNum - vec.size();
for( int i=0; i<len; ++i ) {
    vec.emplace_back("aiueo");
}

アバター
Ketty
記事: 102
登録日時: 10年前

Re: (C++)std::vectorのresize()による初期化について

#11

投稿記事 by Ketty » 9年前

>Blueさん
ありがとうございます(^^)
参考にさせていただきますね。

今回の件では、私にはemplace_backが分かりやすいと思いましたので、
emplace_backを使ってみようと思います。

>h2so5さん
重ね重ねありがとうございます。
h2so5 さんが書きました: すみません、先ほど投稿したコードはreserveに渡した引数とcapacityが一致する保証がないので間違いです。
なるほど、
reserveする前のcapacityが、reserveする値より大きかったらcapacityは増えない(reNumになるとは限らない)から、
ということですね(^^)

ありがとうございました。
解決とさせていただきます。

閉鎖

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