LIFE LOG(ここにはあなたのブログ名)

あつあつ備忘録

ソフトやハード、時にはメカの備忘録をまとめていきます

【C++】コピーコンストラクタ

f:id:AtsuyaKoike:20190524095847p:plain:w300

これまでの方法だと

  • オブジェクトを関数に渡す
  • 関数からオブジェクトを返す

ときに問題が発生した。

具体的には

  • オブジェクトを関数に渡すと、そのオブジェクトのコピーが作成され、オブジェクトを受け付ける関数の引数として使用される
    • 割り当てられたメモリのポインタを含むオブジェクトがある場合、コピーオブジェクトは元のオブジェクトと同じメモリを指す
      • コピーオブジェクトでこのメモリに変更を加えると、元のオブジェクトに影響
    • 関数の終了時に、コピーオブジェクトが破棄されデストラクタが呼び出されることで影響

  • 関数からオブジェクトを返す場合
    • 関数から返す値を保持する一時オブジェクトを作成する
      • 呼び出し元のルーチンに返した後は、スコープを外れデストラクタが呼び出される
      • これが動的メモリの場合に問題が生じる

  • コピーコンストラクタは
    • 1つのオブジェクトを使用して他のオブジェクトを初期化する方法を指定できる
    • コピーコンストラクタは、そのオブジェクトを使って他のオブジェクトを初期化するときに呼出される

study152.cpp

#include <iostream>
#include <cstdlib>
using namespace std;

class array {
    int *p;
    int size;
public:
    array( int sz ) {
        p = new int[sz];
        if( !p ) {
            exit(1);
        }
        size = sz;
        cout << "通常のコンストラクタを使う\n";
    }
    ~array() {
        delete [] p;
    }
    
    array( const array &a );

    void put( int i, int j ) {
        if ( i >= 0 && i < size ) {
            p[i] = j;
        }
    }
    int get( int i ) {
        return p[i];
    }
};

array::array( const array &a ) {
    size = a.size;
    p = new int[a.size];
    if( !p ) {
        exit(1);
    }
    for( int i = 0; i < a.size; i++ ) {
        p[i] = a.p[i];
    }
    cout << "コピーコンストラクタを使用\n";
}

int main() {
    array num(10);
    
    for ( int i = 0; i < 10; ++i ) {
        num.put( i, i );
    }

    for ( int i = 0; i < 10; ++i ) {
        cout << num.get(i);
    }
    cout << "\n";

    array x = num;

    for ( int i = 0; i < 10; ++i ) {
        cout << x.get(i);
    }
    cout << "\n";

    return 0;
}
 ./study152 
通常のコンストラクタを使う
0123456789
コピーコンストラクタを使用
0123456789

study156.cpp

#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;

class strtype {
    char *p;
public:
    strtype( char *s );
    strtype( const strtype &o );
    ~strtype() {
        delete [] p;
    }
    char *get() {
        return p;
    }
};

strtype::strtype( char *s ) {
    int l;
    l = strlen(s) + 1;

    p = new char [l];
    if ( !p ) {
        cout << "メモリ割り当てエラー\n";
        exit(1);
    }
    strcpy( p, s );
}

strtype::strtype( const strtype &o ) {
    int l;
    l = strlen(o.p) + 1;

    p = new char [l];
    if ( !p ) {
        cout << "メモリ割り当てエラー\n";
        exit(1);
    }
    strcpy( p, o.p );
}

void show( strtype x ) {
    char *s;

    s = x.get();
    cout << s << "\n";
}

int main() {
    strtype a("Hello"), b("There");
    
    show(a);
    show(b);

    return 0;
}
  • 関数へオブジェクトを引き渡すときにコピーが作成されるが、その設定をコピーコンストラクタが行う
  • コピー作成時にコピー用のメモリを割り当てる
    • show()終了時にスコープから外れ、x.pが指すメモリが開放されるが、これは関数に渡した元のオブジェクトのメモリとは異なる。




引用・参考はこちら

f:id:AtsuyaKoike:20190524100759j:plain:w200
独習C++ 第4版 https://www.amazon.co.jp/dp/4798119768