web-dev-qa-db-ja.com

クラスのコンパイラによって作成されたすべてのメンバー関数は何ですか?それはいつも起こりますか?

クラスのコンパイラによって作成されたすべてのメンバー関数は何ですか?それはいつも起こりますか?デストラクタのように。私の懸念は、それがすべてのクラスに対して作成されるかどうかであり、デフォルトのコンストラクタが必要なのはなぜですか?

58
Onnesh

C++ 98/03

それらが必要な場合、

  1. 独自のコンストラクターを宣言しない限り、コンパイラーはdefaultコンストラクターを生成します。
  2. コンパイラはcopyconstructor自分で宣言しない限り、あなたのために。
  3. コンパイラーは、copy代入演算子自分で宣言しない限り、あなたのために。
  4. 独自に宣言しない限り、コンパイラはdestructorを生成します。

Péterが有益なコメントで述べたように、これらはすべて、必要な場合にのみコンパイラーによって生成されます。 (違いは、コンパイラーがそれらを作成できない場合、Okは使用されない限りです。)


C++ 11

C++ 11は、C++ 14にも当てはまる次のルールを追加します(towiのクレジット、 this commentを参照してください):

  • コンパイラーはmoveconstructorif
    • ユーザー宣言はありませんcopyconstructor、および
    • ユーザー宣言はありませんcopy代入演算子、および
    • ユーザー宣言なしmove代入演算子および
    • ユーザー宣言デストラクターはありません。
    • notとしてマークされたdeleted
    • すべてのメンバーとベースはmoveableです。
  • move代入演算子:ユーザーが定義されていない場合に生成されます
    • ユーザー宣言はありませんcopyconstructor、および
    • ユーザー宣言はありませんcopy代入演算子、および
    • ユーザーが宣言したものはありませんmoveconstructorおよび
    • ユーザー宣言デストラクターはありません。
    • notとしてマークされたdeleted
    • すべてのメンバーとベースはmoveableです。

これらの規則は、C++ 03の規則よりも少し複雑であり、実際にはより意味があることに注意してください。

上記の内容の理解を容易にするために、ここでThingの項目を示します。

class Thing {
public:
    Thing();                        // default constructor
    Thing(const Thing&);            // copy c'tor
    Thing& operator=(const Thing&); // copy-assign
    ~Thing();                       // d'tor
    // C++11:
    Thing(Thing&&);                 // move c'tor
    Thing& operator=(Thing&&);      // move-assign
};

さらに読むと、もしあなたがC++初心者なら、最後の5つを実装する必要のない設計を考えてください。別名ゼロの規則(by Martinho Fernandes

84
sbi

「作成された」によって「定義された」という意味ですか?

$ 12.1-「デフォルトのコンストラクター(12.1)、コピーコンストラクターとコピー割り当て演算子(12.8)、およびデストラクター(12.4)は特別なメンバー関数です。

「作成済み」が「定義済み」を意味する場合、C++標準の重要な部分は次のとおりです。

-クラスの暗黙的に宣言されたデフォルトコンストラクターは、そのクラスタイプ(1.8)のオブジェクトを作成するために使用されるときに暗黙的に定義されます。

-クラスにユーザーが宣言したデストラクタがない場合、デストラクタは暗黙的に宣言されます。暗黙的に宣言されたデストラクタは、そのクラス型のオブジェクトを破棄するために使用されるときに暗黙的に定義されます。

-クラス定義でコピーコンストラクターが明示的に宣言されていない場合、コピーコンストラクターが暗黙的に宣言されます。暗黙的に宣言されたコピーコンストラクターは、そのクラス型のオブジェクトまたはそのクラス型から派生したクラス型のオブジェクトのコピーからそのクラス型のオブジェクトを初期化するために使用される場合、暗黙的に定義されます。

-クラス定義でコピー代入演算子が明示的に宣言されていない場合、コピー代入演算子が暗黙的に宣言されます。暗黙的に宣言されたコピー割り当て演算子は、そのクラス型のオブジェクトにそのクラス型の値またはそのクラス型から派生したクラス型の値が割り当てられると、暗黙的に定義されます。

2
Chubsdad

デフォルトでは、ユーザーが実装していない場合、コンパイラーはいくつかのメンバー関数をクラスに追加します。これらはビッグ4と呼ばれます。

  • デフォルトコンストラクタ
  • コピーコンストラクタ
  • コピー演算子(割り当て)
  • デストラクタ

メンバーのタイプとリストしたメンバー関数に応じて、それらがすべて生成されるわけではありません。

1
Klaim

他の答えは、作成されたものを教えてくれ、コンパイラは使用された場合にのみそれらを生成するかもしれません。

私の懸念は、すべてのクラスに対して作成されるかどうかです...

なぜ心配なのですか?実行可能ファイルに不要なコードが作成されていると考えていますか?可能性は低いですが、環境で十分に簡単に確認できます。

それとも、おそらくあなたの懸念は、コンストラクタが必要なときにコンストラクタを作成しないかもしれないということでしたか?心配することはありません...それらは必要に応じて常に作成され、ユーザーによって提供されません。

...そして、なぜデフォルトのコンストラクタが必要なのですか?

クラスには、体系的に呼び出す必要のある独自のデストラクタを持つオブジェクトが含まれている場合があるためです。たとえば、与えられた...

struct X
{
    std::string a;
    std::string b;
};

...デフォルトのデストラクタは、aとbのデストラクタが確実に実行されるようにします。

0
Tony Delroy

C++ 17 N4659標準ドラフト

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 6.1「宣言と定義」には、おそらくそれらすべてを要約した注記があります。

4 15.4)メンバー関数。 —終了ノート] [例:指定

#include <string>

struct C {
  std::string s;         // std::string is the standard library class (Clause 24)
};

int main() {
  C a;
  C b = a;
  b = a;
}

実装は、Cの定義を以下と同等にする関数を暗黙的に定義します。

struct C {
  std::string s;
  C() : s() { }
  C(const C& x): s(x.s) { }
  C(C&& x): s(static_cast<std::string&&>(x.s)) { }
  // : s(std::move(x.s)) { }
  C& operator=(const C& x) { s = x.s; return *this; }
  C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
  // { s = std::move(x.s); return *this; }
  ~ C() { }
};

—終了例]

それらが宣言される条件は、次で説明されています: default/copy/move ctorおよびcopy/move assignment operatorの自動生成の条件?

何かにデフォルトが設定されていることを確認するクールな方法は、= defaultで説明したように: クラスの関数宣言後の「デフォルト」とはどういう意味ですか?

以下の例はそれを実行し、暗黙的に定義されたすべての関数も実行します。

#include <cassert>
#include <string>

struct Default {
    int i;
    Default()                          = default;
    Default(const Default&)            = default;
    Default& operator=(Default&)       = default;
    Default& operator=(const Default&) = default;
    Default(Default&&)                 = default;
    Default& operator=(Default&&)      = default;
    ~Default()                         = default;
};

struct Instrument {
    int i;
    static std::string last_call;
    Instrument()                             { last_call = "ctor"; }
    Instrument(const Instrument&)            { last_call = "copy ctor"; }
    Instrument& operator=(Instrument&)       { last_call = "copy assign"; return *this; }
    Instrument& operator=(const Instrument&) { last_call = "copy assign const"; return *this; }
    Instrument(Instrument&&)                 { last_call = "move ctor";  }
    Instrument& operator=(Instrument&&)      { last_call = "move assign"; return *this; }
    ~Instrument()                            { last_call = "dtor"; }
};
std::string Instrument::last_call;

int main() {
    // See what the default constructors are doing.
    {
        // Default constructor.
        Default ctor;
        // i is uninitialized.
        // std::cout << ctor.i << std::endl;
        ctor.i = 1;

        // Copy constructor.
        Default copy_ctor(ctor);
        assert(copy_ctor.i = 1);

        // Copy assignment.
        Default copy_assign;
        copy_assign = ctor;
        assert(copy_assign.i = 1);

        // Copy assignment const.
        const Default const_ctor(ctor);
        Default copy_assign_const;
        copy_assign_const = const_ctor;
        assert(copy_assign_const.i == 1);

        // Move constructor.
        Default move_ctor(std::move(ctor));
        assert(move_ctor.i == 1);

        // Move assignment.
        Default move_assign;
        move_assign = std::move(ctor);
        assert(move_assign.i == 1);
    }

    // Check that the constructors are called by these calls.
    {
        // Default constructor.
        Instrument ctor;
        assert(Instrument::last_call == "ctor");

        // Copy constructor.
        Instrument copy_ctor(ctor);
        assert(Instrument::last_call == "copy ctor");

        // Copy assignment.
        copy_ctor = ctor;
        assert(Instrument::last_call == "copy assign");

        // Copy assignment const.
        const Instrument const_ctor(ctor);
        Instrument copy_assign_const;
        copy_assign_const = const_ctor;
        assert(Instrument::last_call == "copy assign const");

        // Move constructor.
        Instrument move_ctor(std::move(ctor));
        assert(Instrument::last_call == "move ctor");

        // Move assignment.
        Instrument move_assign;
        move_assign = std::move(ctor);
        assert(Instrument::last_call == "move assign");

        // Destructor.
        {
            Instrument dtor;
        }
        assert(Instrument::last_call == "dtor");
    }
}

GitHubアップストリーム

GCC 7.3.0でテスト済み:

g++ -std=c++11 implicitly_defined.cpp