404 Not Found

404 Not Found


nginx

高度なコンストラクタ

[第]28[課では]コンストラクタ[の基礎]を学びました。

[実際の場面では]、[objectをコピー][objectを移動][初期化リストを使って効率を上げる]……といった操作が必要になるかもしれません。

[このレッスン]では、[コンストラクタの][応用的な使い方]を[学びましょう]。


1. デフォルトコンストラクタ

(1) 1.1 デフォルトコンストラクタとは?

デフォルトコンストラクタ[とは][引数なし][の]コンストラクタのこと。

CPP
#include iostream
#include string

class Student {
private:
 std::string name;
 int age;
 
public:
 // ()
 Student() {
 name = "Unknown";
 age = 0;
 std::cout << "..." << std::endl;
 }
};

int main() {
 Student s1; // ✅ callingconstructor
 return 0;
}

💡 [要点]: [もし][コンストラクタを一切記述していない]場合、コンパイラは[自動的に]デフォルトコンストラクタを生成します([何も行いません])。[しかし、もし][いずれかの]コンストラクタを記述している]場合、コンパイラは[デフォルトコンストラクタを][自動的に生成しなくなります]。


(2) 1.2 [問題]:[引数付き]コンストラクタ[を記述した後]、[デフォルトでのコンストラクタ呼び出しができなくなる]

CPP
#include iostream
#include string

class Student {
private:
 std::string name;
 int age;
 
public:
 // ...
 Student(const std::string& n, int a) {
 name = n;
 age = a;
 }
};

int main() {
 Student s1("Alice", 20); // ✅
 Student s2; // ❌:Yesconstructor
 return 0;
}

[修正]: [明示的に]デフォルトコンストラクタを記述するか、[または] = default(C++11)を使用する。

CPP
//:
Student() {
 name = "Unknown";
 age = 0;
}

//: = default(C++11,)
Student() = default;


2. [リスト]の初期化(Initializer List)

(1) 2.1 [リスト]の初期化とは何か?

[初期化リスト]は、[コンストラクタの][コロン][の直後に][メンバー変数を直接初期化する]ものであり、[関数本体][内で値を代入する]ものではありません。

[従来の表記法]([賦]value):

CPP
Student(const std::string& n, int a) {
 name = n; //,
 age = a;
}

[初期化リストの書き方]([推奨]):

CPP
Student(const std::string& n, int a) : name(n), age(a) {
 // ...
}

💡 [強み]:

  1. [効率が高い]([直接初期化]、[デフォルトのコンストラクタで生成してから値を代入するのではなく])
  2. [初期化リストを使用しなければならない場面]:

(2) 2.2 [必須]リストの初期化 [シーン]

[シーン1]:constのメンバー

CPP
#include iostream
#include string

class Student {
private:
 const std::string id; // const
 std::string name;
 
public:
 // ❌:const value
 // Student(const std::string& i, const std::string& n) {
 // id = i; // ❌ const value
 // name = n;
 // }
 
 // ✅:Usinglist
 Student(const std::string& i, const std::string& n) : id(i), name(n) {
 }
};

int main() {
 Student s1("20240001", "Alice");
 return 0;
}


3. コピーコンストラクタ(Copy Constructor)

(1) 3.1 コピーコンストラクタとは何か?

コピーコンストラクタとは、[ある]オブジェクト[を別の]オブジェクト[で初期化する]ためのコンストラクタです。

[トリガーシーン]:

  1. [ある]オブジェクト[を使って別の]オブジェクトを初期化する
  2. 関数[引数]は値渡し([コピーされる])
  3. 関数[戻り値]オブジェクト([コピー可能])

▶ サンプル 1: [デフォルトの]コピーコンストラクタ (難易度 ⭐)

CPP
#include iostream
#include string

class Student {
public:
 std::string name;
 int age;
 
 // ()
};

int main() {
 Student s1;
 s1.name = "Alice";
 s1.age = 20;
 
 Student s2 = s1; // ✅ callingcopy constructor
 std::cout << "s2:" << s2.name << std::endl; // Alice
 std::cout << "s2:" << s2.age << std::endl; // 20
 
 return 0;
}
▶ 試してみよう

💡 [要点]: [もし][コピーコンストラクタを定義していない場合]、コンパイラは[自動的に]([メンバーごとに]コピー)を生成します。


(2) 3.2 [カスタム]コピーコンストラクタ

CPP
#include iostream
#include string

class Student {
private:
 std::string name;
 int age;
 
public:
 // ...
 Student(const std::string& n, int a) : name(n), age(a) {
 std::cout << ":" << name << std::endl;
 }
 
 // ...
 Student(const Student& other) : name(other.name), age(other.age) {
 std::cout << ":" << name << std::endl;
 }
};

int main() {
 Student s1("Alice", 20);
 Student s2 = s1; // ...
 
 return 0;
}

出力:



4. [浅いコピー] 対 [深いコピー]

(1) 4.1 [問題]:[デフォルトのコピーは]「[浅いコピー]」

[もし]クラス[に]ポインタメンバ([ヒープメモリを]指す)がある場合、[デフォルトの]コピーコンストラクタは[単に][ポインタの]値(アドレス)を[コピー]するだけで、[ポインタが指す内容]は[コピー]しません。

CPP
#include iostream
#include cstring

class MyClass {
public:
 char* data;
 
 MyClass() {
 data = new char[5];
 std::strcpy(data, "Hello");
 }
 
 // ❌ YesDefinecopy constructor(Using's)
 
 ~MyClass() {
 delete data;
 }
};

int main() {
 MyClass obj1;
 MyClass obj2 = obj1; //:obj1.data obj2.data
 
 // main,obj2 ( data)
 // obj1 ( data,)→!
 return 0;
}

💡 [これが]「[浅いコピー]」の問題です――[2つの]オブジェクトの[ポインタが][同じ]メモリ領域を[指しているため]、[破棄時に][重複して解放]されてしまいます。


(2) 4.2 [解決策]:[カスタムディープコピー]

CPP
#include iostream
#include cstring

class MyClass {
public:
 char* data;
 
 MyClass() {
 data = new char[5];
 std::strcpy(data, "Hello");
 }
 
 // ()
 MyClass(const MyClass& other) {
 data = new char[std::strlen(other.data) + 1];
 std::strcpy(data, other.data);
 }
 
 ~MyClass() {
 delete data;
 }
};

int main() {
 MyClass obj1;
 MyClass obj2 = obj1; //:obj2.data points
 
 std::cout << "obj1.data = " << obj1.data << std::endl;
 std::cout << "obj2.data = " << obj2.data << std::endl;
 
 return 0;
}

💡 [重要]: [もし]クラス[に]ポインタ型のメンバがある場合、[コピーコンストラクタを定義する必要がある]([ディープコピーを実装する])。



5. 移動コンストラクタ(C++11、[選択科目])

(1) 5.1 移動コンストラクタとは何か?

ムーブコンストラクタ[は] C++11 [で導入された新機能]――「[他人のリソースを]」[コピーするのではなく]。

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

CPP
#include iostream
#include string

class MyClass {
private:
 std::string* data;
 
public:
 // ...
 MyClass(const std::string& s) {
 data = new std::string(s);
 std::cout << "..." << std::endl;
 }
 
 // (C++11)
 MyClass(MyClass&& other) noexcept : data(other.data) {
 other.data = nullptr; // other ()
 std::cout << "..." << std::endl;
 }
 
 ~MyClass() {
 delete data;
 }
};

int main() {
 MyClass obj1("Hello");
 MyClass obj2 = std::move(obj1); // ...
 
 return 0;
}

💡 [ヒント]: ムーブコンストラクタは[高度なトピック]です。[初心者はまずは概要を把握し]、[後で詳しく学ぶ]ことをお勧めします。



6. 演習:[完全版] Student クラス

▶ サンプル 2: [コピーコンストラクタを備えた] Student クラス (難易度 ⭐⭐)

CPP
#include iostream
#include string

class Student {
private:
 std::string name;
 int age;
 
public:
 // ...
 Student(const std::string& n, int a) : name(n), age(a) {
 std::cout << ":" << name << std::endl;
 }
 
 // ...
 Student(const Student& other) : name(other.name), age(other.age) {
 std::cout << ":" << name << std::endl;
 }
 
 // ...
 ~Student() {
 std::cout << ":" << name << std::endl;
 }
 
 void introduce() {
 std::cout << "My name is" << name << ",I'm" << age << "years old。" << std::endl;
 }
};

int main() {
 Student s1("Alice", 20);
 Student s2 = s1; // ...
 
 s1.introduce();
 s2.introduce();
 
 return 0;
}
▶ 試してみよう

❓ よくある質問

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

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

Q:[リストの初期化と]関数本体での[値の代入]では、[効率にどれほどの差がある]か? A:[基本]型の場合、[それほど差はない]。[しかし] STL型(std::stringstd::vector)の場合、[大きな差がある]――

  • 関数本体:[最初に空の]オブジェクトを初期化し、[その後]valueを代入する([必要に応じて]メモリを割り当て、[内容をコピーする])。
  • [初期化リスト]:[直接初期化]([1回のみ割り当て]メモリ)

[提案]: [一律に][初期化リスト]を使用する。

Q:移動コンストラクタ[は何に役立つ]? A:[一時的な]オブジェクトの[コピー効率を向上させる]。

[例えば]関数が[大きな]オブジェクトを[返す]場合、moveコンストラクタ[を使用することで][ディープコピー]([直接]一時的なオブジェクトのメモリを「盗む」)を[回避できる]。


📖 まとめ


📝 練習問題

  1. 初心者(難易度 ⭐): Bookというクラスを定義し、[書籍名]と[著者2名]のメンバを含める。[初期化リスト][を使用して]コンストラクタを記述する。

  2. 中級(難易度 ⭐⭐): [カスタム] Book クラスのコピーコンストラクタ、[ディープコピーを実装]([書籍名が] char* [で保存されている] と仮定し、[新しい]メモリを割り当てる必要がある)。

  3. 上級(難易度 ⭐⭐⭐): [定義する] DynamicArray クラス([動的]配列)、[以下を含む]:

[実装]:

[テスト]:[作成] DynamicArray obj1(10)、[再利用] obj1、[初期化] obj2、[2つの]object[が] data [異なる]memoryaddressを指していることを検証する。


7. 🚀 次は

[コンストラクタの]高度な使い方を[習得しました]。[次は] 演算子のオーバーロード([第]30[回])を学びます―― [カスタム]型でも+-<< [などの]演算子を使えるようにします!

Web-Tutorial.com

Web-Tutorial 技術チーム

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

100%