ストラウストラップのプログラミング入門(27) 18章「ベクタと配列」

ストラウストラップのプログラミング入門』を読む。今日は、18章「ベクタと配列」の18.3まで。

前章のドリルで悩んでいたvectorのコピーについては、この章で本格的に扱われていました。

ところで、ソ ラ ノートが他人事とは思えない今日この頃。あれほどのコンテンツ性がこの日記にはない上に、本に載ってるコードも引用しまくりんぐです。大丈夫かしらん。。。まぁ、引用元はくどいくらい明らかにしていきますです。

読書メモ

標準のvectorのコピーはディープコピーである

つまり、前回の読書日記の追記に書いたコードで、まるでシャローコピーを行うクラスに対するかのような心配をして、値を1個ずつせっせとコピーしようとしていましたが、

int main(){

    vector<int> vv; // drill(13_10)
    for(int i = 0; i < 10; ++i){
        if(i == 0){
            vv.push_back(1);
        }else{
            vv.push_back(vv[i - 1] * 2);
        } 
    }
    
    vector<int>* vv2 = new vector<int>(10);
    
    for(int i = 0; i < (*vv2).size(); ++i){    // ★'
        (*vv2)[i] = vv[i];
    }
}

★'の箇所は、次のコードで十分なのですね。

    vv2 = &vv;    // ★
コピーコンストラクタとコピー代入

18.3.2「コンストラクタとデストラクタのデバッグ」では、コンストラクタとデストラクタの動きを確認し、理解するためのサンプルコードが提示されます。そこで、このコードを写経し、実行前に出力を予想した上で、実行結果と見比べました。すると案の定、理解不十分な点が浮き彫りになり、いいカンジに厭な感じになりました(……)。

次のコード(p.572)で、

struct X {
    int val;
    void out(const string& s, int nv)
        { cerr << this << "->" << s << ": " << val << " (" << nv << ")\n"; }
        
    X(){ out("X()", 0); val = 0; }
    X(int v) { out("X(int)", v); val = v; }
    X(const X& x) { out("X(X&) ", x.val); val = x.val; }
    X& operator=(const X& a)
        { out("X::operator=()", a.val); val = a.val; return *this; }
    ~X() { out("~X()", 0); }
};

X copy(X a) { return a; }

int main(){
    X loc(4);
    X loc2 = loc;
    //(以下略)
}

loc2を宣言してlocを代入している行では、コピーコンストラクタが実行されます。「=」という演算子に惑わされて、operator=()が呼ばれるのだとばかり思っていました。

左辺でクラスの宣言が行われたときは、コピー代入ではなく、コピーコンストラクタが呼ばれるのですね。

また、コピー代入の右辺で名前を付けずに作成したオブジェクトは、代入が済んだら破棄されることも理解しました。

コピー代入の実行箇所が謎

上記のmain関数は、こんな調子でどんどん続きます。

int main(){
    X loc(4);
    X loc2 = loc;
    loc = X(5);
    loc2 = copy(loc);
    //(以下略)
}

copy関数を使っている行で、コピー代入が2回呼ばれています。ナニコレ。いつの間に…。operator=()が呼ばれる前に、copy(loc)のどこかで呼ばれているのは間違いないのですが・・・よくワカラン。

無理やり考えてみると、

  1. copy関数の呼び出しの引数として、loc変数を評価した段階で、まず1回コピー代入が実行される
  2. 「copy(loc)」全体の評価が完了する段階で、もう1回コピー代入が実行される
  3. copy(loc)が分かれば、もう単体locはいらないので、1回目のデストラクタが走り、locのためのメモリが解放される
  4. operator=()が実行される
  5. copy(loc)自体も不要になるので、2回目のデストラクタが走り、copy(loc)の評価結果(?)が占めていたメモリが解放される

という感じでしょうか。サテハテ。。。