404 Not Found

404 Not Found


nginx

コピー制御

[第]29[課では]コンストラクタ[の応用]について学びました。

[ただし、もし][objectをコピー][valueobjectに代入][objectを移動]する場合、C++には[一連の][コピーに関するルール]があります。

[このルールを理解して]、[初めて正しい]クラスを作成できる。


1. [三五の法則](Rule of Three/Five)

(1) 1.1 [三五法則]とは何か?

[もしあなたの]クラス[が以下のいずれかの]関数を[カスタマイズする必要がある]場合、[その際は][関連するすべての]関数を[カスタマイズしなければなりません]:

[バージョン] [管理が必要な]関数 [説明]
[三法則](C++98) 1. デストラクタbr 2. コピーコンストラクタbr 3. コピー代入演算子 [動的メモリ][を使用する場合に必要]
[五つの法則](C++11) [三つの法則] +br4. 移動コンストラクタbr5. 移動代入演算子 C++11 [導入]移動セマンティクス

(2) 1.2 [なぜ「3-5の法則」が必要なのか]?

[そのうちの1つをカスタマイズする必要がある場合]、[説明]クラス[内には][特別な処理が必要なリソース]([例えば]動的メモリ、ファイル[ハンドル])が含まれています。

例:[三法則に従わないことによる結果] (難易度 ⭐⭐)

▶ サンプル 2: codeexample (難易度 ⭐)

CPP
#include iostream
#include cstring

class BadString {
private:
 char* data;
 
public:
 // ...
 BadString(const char* s) {
 data = new char[strlen(s) + 1];
 strcpy(data, s);
 }
 
 // ❌ YesDefinecopy constructor、value、destructor
 // ()
};

int main() {
 BadString s1("Hello");
 BadString s2 = s1; // ❌:s1.data and s2.data
 
 // main,s2 ( data)
 // s1 ( data,)→!
 
 return 0;
}
▶ 試してみよう

[結果]: プログラムが[クラッシュ]する([同一のメモリ領域を繰り返し解放]するため)。

[修正]: [3つの法則に従う]、[3つすべてをカスタマイズする]関数。



2. コピー代入演算子 *

基本的な使い方

CPP
#include iostream
#include cstring

class MyString {
private:
 char* data;
 
public:
 // ...
 MyString(const char* s) {
 data = new char[strlen(s) + 1];
 strcpy(data, s);
 }
 
 // ...
 MyString(const MyString& other) {
 data = new char[strlen(other.data) + 1];
 strcpy(data, other.data);
 }
 
 // ...
 MyString& operator=(const MyString& other) {
 if (this != &other) { // ...
 delete data; // ...
 data = new char[strlen(other.data) + 1];
 strcpy(data, other.data);
 }
 return *this;
 }
 
 // ...
 ~MyString() {
 delete data;
 }
};

int main() {
 MyString s1("Hello");
 MyString s2("World");
 
 s2 = s1; // ✅ callingvalue
 
 return 0;
}

💡 [重点]: 代入演算子のコピー[確認要][自己代入]値if (this != &other))。



3. 移動セマンティクス(C++11)*

(1) 3.1 なぜ[移動]が必要なのでしょうか?

[もし][一時的な]オブジェクト([まもなく破棄される]もの)があるなら、[その内容をコピーするのは無駄]だ――[それよりも]そのリソースを「[盗む]」ほうがよい。

例:[移動を使って不要なコピーを避ける] (難易度 ⭐⭐)

CPP
#include iostream
#include cstring

class MyString {
private:
 char* data;
 
public:
 // (C++11)
 MyString(MyString&& other) noexcept {
 data = other.data; // ...
 other.data = nullptr; // other
 }
 
 // (C++11)
 MyString& operator=(MyString&& other) noexcept {
 if (this != &other) {
 delete data; // ...
 data = other.data; // ...
 other.data = nullptr; // other
 }
 return *this;
 }
};

int main() {
 MyString s1("Hello");
 MyString s2 = std::move(s1); // ✅ callingconstructor
 // s1.data nullptr,s2.data
 
 return 0;
}

💡 [ヒント]: std::move() [lvalueを]rvalue参照に[変換]することで、[moveコンストラクタ[または]move代入演算子]を[呼び出す]。



4. = デフォルト [和] = 削除*

(1) 4.1 [用] = デフォルト [让]コンパイル[器がデフォルトの実装を生成する]*

[もしあなたの]クラス[に特別なコピーロジックが必要ない場合]= default [を使って]コンパイラに[デフォルトのものを]生成させることができます。

CPP
#include iostream
#include string

class Student {
private:
 std::string name;
 int age;
 
public:
 //、、
 Student(const Student& other) = default;
 Student& operator=(const Student& other) = default;
 ~Student() = default;
};

int main() {
 Student s1;
 Student s2 = s1; // ✅ callingdefault copy constructor
 
 return 0;
}

💡 [ヒント]: [もし]クラス[の中に] STL 型(std::stringstd::vector [など])しか含まれていない場合は、= default [を使えば] [十分]です。


(2) 4.2 [用] = delete [コピー禁止]*

[もし]class[がコピーされるのを防ぎたい場合]([シングルトンパターンなど])、= delete [を使用して] [コピーを禁止]することができます。

CPP
#include iostream

class Singleton {
private:
 Singleton() {} // ...
 
public:
 // ...
 Singleton(const Singleton&) = delete;
 Singleton& operator=(const Singleton&) = delete;
 
 static Singleton& getInstance() {
 static Singleton instance;
 return instance;
 }
};

int main() {
 Singleton& s = Singleton::getInstance();
 // Singleton s2 = s; // ❌ compilation:copy constructor
 
 return 0;
}


5. 演習:[完全版] MyString クラス*

▶ サンプル 1: [完全な] MyString クラスを実装する (難易度 ⭐⭐⭐)

CPP
#include iostream
#include cstring

class MyString {
private:
 char* data;
 int length;
 
public:
 // ...
 MyString(const char* s = "") {
 length = strlen(s);
 data = new char[length + 1];
 strcpy(data, s);
 }
 
 // ...
 MyString(const MyString& other) {
 length = other.length;
 data = new char[length + 1];
 strcpy(data, other.data);
 }
 
 // ...
 MyString(MyString&& other) noexcept {
 data = other.data;
 length = other.length;
 other.data = nullptr;
 other.length = 0;
 }
 
 // ...
 MyString& operator=(const MyString& other) {
 if (this != &other) {
 delete data;
 length = other.length;
 data = new char[length + 1];
 strcpy(data, other.data);
 }
 return *this;
 }
 
 // ...
 MyString& operator=(MyString&& other) noexcept {
 if (this != &other) {
 delete data;
 data = other.data;
 length = other.length;
 other.data = nullptr;
 other.length = 0;
 }
 return *this;
 }
 
 // ...
 ~MyString() {
 delete data;
 }
 
 // ...
 void print() const {
 if (data) std::cout << data;
 }
 
 int size() const {
 return length;
 }
};

int main() {
 MyString s1("Hello");
 MyString s2 = s1; // ...
 
 s1.print(); // Hello
 std::cout << std::endl;
 
 s2.print(); // Hello
 std::cout << std::endl;
 
 return 0;
}
▶ 試してみよう

❓ よくある質問

Q:[どのような場合に]コピーコンストラクタを定義すべきか? A:[クラス内に]([動的メモリを]指す)ポインタ型のメンバがある[場合]。

クラスに基本型(intdouble)[または] STL型(std::stringstd::vector)しか含まれていない場合は、[独自に実装する必要はない]――コンパイラが[自動的に正しく動作するコードを生成する](STL型は[ディープコピーを独自に実装している])。

Q:=default [和] =delete [どんな役割がある]? A:> - = default:コンパイラに[デフォルトの][実装]([メンバーごとの]コピー)を[生成]させる > - = delete:[特定の]関数を[無効化]する([コピーの禁止]など)

[推奨] = default [手書きのコピーロジックの代替]([特別なロジックが必要ない場合])。

Q:[コピーコンストラクタを記述しない場合]、[どうなる]? A:コンパイラが[自動的に][デフォルトの]コピーコンストラクタを生成する([メンバーごとに]コピーを行う)。

[ただし、]クラス[に]ポインタのメンバがある場合、[デフォルトでは][浅いコピー]が行われ([ポインタの]値のみがコピーされ、[ポインタが指す内容はコピーされない])、[その結果、メモリが重複して解放されてしまう]。


📖 まとめ


📝 練習問題

  1. 初心者(難易度 ⭐): Pointというクラスを定義し、xとyの[2つの]メンバを[含む]。
  1. 中級(難易度 ⭐⭐): [定義する] DynamicArray クラス([動的]配列)、[以下を含む]:

[5つのルールに従い]、[すべての特殊な]メンバ関数を実装する。

  1. 上級(難易度 ⭐⭐⭐): [拡張] DynamicArray クラス、[追加]:
  1. コピーコンストラクタ:[同]typeobject[を基に新しい]objectを初期化する

6. 🚀 次は

[コピー制御と]moveセマンティクスを学んだので、[次は] 継承([第]31[回])を学びます―― [これにより]クラス[間で]「[親子]」[関係]を形成し、[コードの][再利用]を実現します!

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%