c++ の class は、最低限必要とするコンストラクタやメンバー関数がいろいろあります。
加えて "自動的に作成される特殊メンバ関数"
なんてものがあり、さらに面倒なことに自動的に生成されるルールがあるため、自動生成されたり、自動生成されなかったりします。正直、お手本のソースコードでも用意しておかないと難しい内容です。
本ページでは、c++ の class の雛形を用意して、これをもとにして必要な修正を行うだけである程度のコーディングを行えるようにすることを目指します。
クラスには、暗黙的に作成されるメンバ関数があります。以下がそれに該当します。
= default は、これらに対して以下のように明示的にデフォルトのものを使う指定ができます。
class S {
S() = default;
}
クラスにコンストラクタが1つも宣言されていない場合、デフォルトコンストラクタ、コピーコンストラクタ、およびムーブコンストラクタが暗黙的に生成されます。しかし、コンストラクタが1つでも宣言されると、それらは暗黙的に生成されなくなります。 = default を用いることで、これらのメンバ関数を暗黙的に生成されたものと同じように生成できます。
= delete は、コンパイラが暗黙的に生成する特殊なメンバ関数の削除を指定します。例えば、コピーはできないがムーブは可能なクラス、は以下のように定義します。
class S {
// コピーできない
S(const S&) = delete;
S& operator=(const S&) = delete;
// ムーブは可能
S(S&& s) {
・・・
}
};
= delete は、特殊メンバ関数に対してだけでなく、通常の関数に対しても指定できます。その場合、delete 指定された関数オーバーロードが呼び出されると、コンパイルエラーになります。
一番シンプルであろうテンプレートを以下に示します。
c++11 以降を前提に、まずは default
指定で全て記載した例を示します。
"自動的に作成される特殊メンバ関数" を全て default
指定しているので、自動的に作成されるメンバ関数はありません。
| コンパイラ : | g++, | 13.3.0 |
| OS : | Ubuntu, | 24.04 |
[プログラムソース "class_template_01.cpp"]
#include <utility> // std::move
class Base {
public:
Base() = default; // default constructor
virtual ~Base() = default; // virtual destructor
Base(const Base&) = default; // copy constructor
Base& operator = (const Base&) = default; // copy operator
Base(Base&&) = default; // move constructor
Base& operator = (Base&&) = default; // move operator
};
int main()
{
Base b1; // call 'default constructor'.
Base b2(b1); // call 'copy constructor'.
Base b3 = b2; // call 'copy operator'.
Base b4(Base{}); // call 'move constructor'.
Base b5 = Base(); // call 'move operator'.
Base b6;
b6 = b1; // call 'copy operator'.
Base b7;
b7 = std::move(b1); // call 'move operator'.
return 0;
}
下記コマンドでビルドします。
$ g++ -std=c++23 -Wall -Wextra -Wpedantic class_template_01.cpp -o class_template_01.out
実行結果は以下の通り・・・何も起こりません。残念ながらそういうプログラムです。
$ ./class_template_01.out $
ちょっとだけプログラムを追加して、コピーやムーブしていることを確認してみたいと思います。
[プログラムソース "class_template_02.cpp"]
#include <utility> // std::move
#include <string>
#include <iostream>
class Base {
public:
std::string name = "default_name";
public:
Base() = default; // default constructor
virtual ~Base() = default; // virtual destructor
Base(const Base&) = default; // copy constructor
Base& operator = (const Base&) = default; // copy operator
Base(Base&&) = default; // move constructor
Base& operator = (Base&&) = default; // move operator
};
int main()
{
Base b1; // call 'default constructor'.
b1.name = "b1";
Base b2(b1); // call 'copy constructor'.
Base b3 = b2; // call 'copy operator'.
Base b4(Base{}); // call 'move constructor'.
Base b5 = Base(); // call 'move operator'.
Base b6;
b6 = b1; // call 'copy operator'.
Base b7;
b7 = std::move(b1); // call 'move operator'.
// 確認
std::cout << "b1.name: " << b1.name << std::endl;
std::cout << "b2.name: " << b2.name << std::endl;
std::cout << "b3.name: " << b3.name << std::endl;
std::cout << "b4.name: " << b4.name << std::endl;
std::cout << "b5.name: " << b5.name << std::endl;
std::cout << "b6.name: " << b6.name << std::endl;
std::cout << "b7.name: " << b7.name << std::endl;
return 0;
}
下記コマンドでビルドします。
$ g++ -std=c++23 -Wall -Wextra -Wpedantic class_template_02.cpp -o class_template_02.out
実行結果を以下に記載します。
$ ./class_template_02.out b1.name: b2.name: b1 b3.name: b1 b4.name: default_name b5.name: default_name b6.name: b1 b7.name: b1 $
厳密な確認ではないですが、期待通りに動作しているであろうことを確認できました。
前章では、= default を積極利用して class を作ってみました。
本章では、同じ内容のプログラムを = default を使用せずに作成してみます。
| コンパイラ : | g++, | 13.3.0 |
| OS : | Ubuntu, | 24.04 |
作成したプログラムを以下に記載します。
それなりに注意を要するプログラムであることがわかります。
[プログラムソース "class_template_03.cpp"]
#include <utility> // std::move
#include <string>
#include <iostream>
class Base {
public:
std::string name = "default_name";
public:
// 明示的な default constructor(元の = default と同等)
Base()
{
}
// 明示的な virtual destructor
virtual ~Base()
{
}
// 明示的な copy constructor
Base(const Base& other)
: name(other.name)
{
}
// 明示的な copy operator
Base& operator=(const Base& other)
{
if (this != &other) {
name = other.name;
}
return *this;
}
// 明示的な move constructor
Base(Base&& other) noexcept
: name(std::move(other.name))
{
}
// 明示的な move operator
Base& operator=(Base&& other) noexcept
{
if (this != &other) {
name = std::move(other.name);
}
return *this;
}
};
int main()
{
Base b1; // call 'default constructor'.
b1.name = "b1";
Base b2(b1); // call 'copy constructor'.
Base b3 = b2; // call 'copy operator'.
Base b4(Base{}); // call 'move constructor'.
Base b5 = Base(); // call 'move operator'.
Base b6;
b6 = b1; // call 'copy operator'.
Base b7;
b7 = std::move(b1); // call 'move operator'.
// 確認
std::cout << "b1.name: " << b1.name << std::endl;
std::cout << "b2.name: " << b2.name << std::endl;
std::cout << "b3.name: " << b3.name << std::endl;
std::cout << "b4.name: " << b4.name << std::endl;
std::cout << "b5.name: " << b5.name << std::endl;
std::cout << "b6.name: " << b6.name << std::endl;
std::cout << "b7.name: " << b7.name << std::endl;
return 0;
}
下記コマンドでビルドします。
$ g++ -std=c++23 -Wall -Wextra -Wpedantic class_template_03.cpp -o class_template_03.out
実行結果を以下に記載します。
$ ./class_template_03.out b1.name: b2.name: b1 b3.name: b1 b4.name: default_name b5.name: default_name b6.name: b1 b7.name: b1 $
前章と同じ結果になることを確認できました。
本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。
| 2025-11-06 | - | 新規作成 |