自動定義と手動定義
自分で定義していない場合
この場合、
- デフォルトコンストラクタ
- コピーコンストラクタ
- デストラクタ
- コピー代入演算子
が自動で定義されます。
1 2 3 4 5 6 7 8 9 10 | class SerialCode { //空っぽだが... }; int main() { //普通に使える SerialCode a; //デフォルトコンストラクタ呼び出し SerialCode b(a); //コピーコンストラクタ呼び出し a = b; //コピー代入演算子呼び出し } |
自動定義で、コピー関係(コピーコンストラクタ、コピー代入演算子)は、クラスのメンバ変数を全てコピーしてくれています。自分で定義するときは、この点に注意しましょう。
自分で定義した場合
1~4の内、自分で定義した部分は、自動で定義されません。
注)引数付きコンストラクタを定義するとデフォルトコンストラクタは自動で定義されません
1 2 3 4 5 6 7 8 9 10 11 12 13 | class SerialCode { public: SerialCode(int num) {}; //①引数付きコンストラクタ SerialCode(const SerialCode&) {}; //②コピーコンストラクタ ~SerialCode() {}; //③デストラクタ //④コピー代入演算子 SerialCode& operator=(const SerialCode& v) { return *this; }; }; int main() { SerialCode a; //エラー デフォルトコンストラクタは存在しない SerialCode b(0); //OK } |
コピー禁止
例として、シリアルコードは一つ限りでコピーの必要はないとします。余計なミスを起こさないためにもコピーを禁止したい。
これを解決するのが、先ほどの手動定義です。コピー関係(コピーコンストラクタ、コピー代入演算子)をpublicではなくprivateで宣言すればいいのです。
1 2 3 4 5 6 7 8 9 10 11 | class SerialCode { private: SerialCode(const SerialCode&); //②コピーコンストラクタ SerialCode& operator=(const SerialCode& v); //④コピー代入演算子 }; int main() { SerialCode a; SerialCode b(a); //②がprivateなので呼び出せずエラー b = a; //④がprivateなので呼び出せずエラー } |
“private” で “宣言” の意味
アクセスエラーとリンクエラーの2重エラーでコピーできなくしています。
private
9行、10行目にクラス外で呼び出しているのでアクセスエラーがでます。
定義ではなく宣言
定義していない(関数の後に{}を付ていない)場合を宣言といいます。宣言しただけの関数を呼び出すとリンクエラーがでるので、privateの条件をクリア(たとえば、クラス内でコピーしようと)したとしても、事実上呼び出すことが不可能になります。
リンクエラーをコンパイルエラーに
リンクエラーは実行するまでエラーを吐いてくれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //コピー禁止 親クラス class Uncopyable { protected: //生成と破棄はできるように Uncopyable() {}; ~Uncopyable() {}; private: Uncopyable(const Uncopyable&); Uncopyable& operator=(const Uncopyable& v); }; //継承した class SerialCode :private Uncopyable{ public: SerialCode() {}; //コンストラクタ ~SerialCode() {}; //デストラクタ void copy(SerialCode ©) { *this = copy; //privateなので アクセスエラー } }; |
継承よって、先ほどのリンクエラーをアクセスエラーにすることができます。
おわりに
簡潔なソースコードを書くためにも自動定義で十分な場合は、それに任せましょう。
参考
「Effective C++ 第3版 5項・6項」