C++のvector

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
大工

C++のvector

#1

投稿記事 by 大工 » 14年前

こんばんわ,

C++の質問してもいいですよね?

下記のソースプログラム(program1)の実行結果が,以下ようになりました.

0xbfefc33f call
0xbfefc33d call
0x9335008 now
0xbfefc33d call
0x9335009 now
0xbfefc33d call
0x933500a now
0xbfefc33d call
0x933500b now
0xbfefc33d call
0x933500c now
0x9335008 call
0x9335009 call
0x933500a call
0x933500b call
0x933500c call

ここでいくつか質問なんですが,

1.なぜなにも操作していないのに1行目でデストラクタが呼ばれてるのでしょうか?
2.t = Test();配列の初期化をこのようにしてるのですが,毎回デストラクタが呼ばれるのはなぜでしょうか?

program1
#include<iostream>
#include<vector>

class Test {

public:
  ~Test() {std::cout << this << " call" << std::endl;}
};

int main(void) {

  std::vector<Test> t(5);

  for(int i = 0; i < 5; i++) {

    t = Test();
    std::cout << &t << " now" << std::endl;
  }

  return 0;
}


プログラムを少し改造して

#include<iostream>
#include<vector>

class Test {

public:
  ~Test() {std::cout << this << " call" << std::endl;}
};

int main(void) {

  std::vector<Test> t(5);

  // 1回目
  for(int i = 0; i < 5; i++) {

    t = Test();
    std::cout << &t << " now" << std::endl;
  }
  
  std::cout << " = " << std::endl;

  // 2回目
  for(int i = 0; i < 5; i++) {

    t = Test();
    std::cout << &t << " now" << std::endl;
  }

  return 0;
}


このようにします.
配列tを2回初期化していますが,1回目で確保されたメモリ領域はまだ確保されたままですよね?
だとしたら,かなり無駄なのでt.clear();をしてみたんですが,結果が
#clear()関数は配列のサイズまで変化しませんよね?


0xbfda0fcf call
0xbfda0fcd call
0x9e5a008 now
0xbfda0fcd call
0x9e5a009 now
0xbfda0fcd call
0x9e5a00a now
0xbfda0fcd call
0x9e5a00b now
0xbfda0fcd call
0x9e5a00c now
=
0x9e5a008 call
0x9e5a009 call
0x9e5a00a call
0x9e5a00b call
0x9e5a00c call
0xbfda0fcc call
0x9e5a008 now
0xbfda0fcc call
0x9e5a009 now
0xbfda0fcc call
0x9e5a00a now
0xbfda0fcc call
0x9e5a00b now
0xbfda0fcc call
0x9e5a00c now

このようになりました.=以降が2回目なんですが,たしかにclear後はデストラクタが呼ばれていますが
3.プログラムが終了するときにデストラクタが呼ばれていません.これはなにがおこってるのでしょうか?

ヨロシクオネガイシマス...

めるぽん

Re:C++のvector

#2

投稿記事 by めるぽん » 14年前

t = Test(); というのは、
1.Test() によって Test という一時オブジェクトが作られる(A とする)
2.t = Test() によって A が t にコピーされる
3.A が破棄される(デストラクタが呼ばれる)
という動作になります。なので一時オブジェクトのデストラクタが呼ばれることになります。(質問2の答え)

質問1は、vector<Test> t(5) と初期化している段階で呼ばれるのですが、これは
vector(size_type n, const T& value = T(), const Allocator& = Allocator());
というコンストラクタが呼ばれます(デフォルト引数は分かりますよね?)。2番目の引数が T() になっているので、これによって一時オブジェクトが作られ、そしてさっきと同じようにこの行を抜けた後に破棄されるので、デストラクタが呼ばれることになります。

program2 のコードは多分 " = " の出力の下で t.clear() を実行してるんですよね?
そうだとすれば2回目の処理は不正な処理になります(t.resize をしているのなら別ですが)。

>だとしたら,かなり無駄
2回目は、1回目で確保された領域を上書きするだけなので、無駄にはならないと思います。

>#clear()関数は配列のサイズまで変化しませんよね?
サイズは変化します。キャパシティは変化しません。
メモリが確保されているだけの領域(キャパシティ)と、初期化されて使える状態になっている領域(サイズ)は別です。前者の領域は t.capacity() で、後者の領域は t.size() で調べられます。t.push_back 等を使って、どういう風に変化するのか見てみるといいかもしれません。
t[n] とアクセスできるのは、初期化されて使える状態になっている領域だけです(つまり 0~t.size()-1 まで)。

質問3ですが、t.clear によって全部の Test オブジェクトが破棄されて、t.size は 0 になって、t の保持しているオブジェクトは1つも無い状態になっているので、プログラムを終了する際に呼び出す Test のデストラクタはありません。

dic

Re:C++のvector

#3

投稿記事 by dic » 14年前

clearが見当たりませんが・・・

私の開発環境 VC++6 では下のようになりました

0012FF58 call
0012FF54 call
004802C0 now
0012FF54 call
004802C1 now
0012FF54 call
004802C2 now
0012FF54 call
004802C3 now
0012FF54 call
004802C4 now
=
0012FF50 call
004802C0 now
0012FF50 call
004802C1 now
0012FF50 call
004802C2 now
0012FF50 call
004802C3 now
0012FF50 call
004802C4 now
004802C0 call
004802C1 call
004802C2 call
004802C3 call
004802C4 call

デストラクタ呼ばれてますね

大工

Re:C++のvector

#4

投稿記事 by 大工 » 14年前

Allocatorに関して、まだ勉強不足ですが引数を見る限りデフォルトコンストラクタが呼ばれてるので納得です。

clearはキャパシティとサイズに違いがあったんですね。clearしてからresizeすれば問題ないのでしょうか?

>だとしたら,かなり無駄

この部分ですが、
std::vector<std::vector<int> > t(1);
std::vector<int> s;

for(int i = 0; i < 5; i++) {

  t[0] = s.rezie(i);
}
#コンパイルしていないので、ちゃんと動くかわかりませんが

このようにしてもt[0]の指す領域は上書きされるだけという認識でいいのでしょうか?

どうやら私はポインタ配列のような感覚があり以前に入力した値はちゃんと解放してから
入力しなければならないと思っているようです。

大工

Re:C++のvector

#5

投稿記事 by 大工 » 14年前

めるぽんさん、dicさん返信ありがとうございます。

閉鎖

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