65

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

4

5 に答える 5

90

C ++ 98/03

それらが必要な場合は、

  1. 独自のコンストラクターを宣言しない限り、コンパイラーはデフォルトのコンストラクターを生成します。
  2. 独自に宣言しない限り、コンパイラはコピー コンストラクタを生成します。
  3. 独自に宣言しない限り、コンパイラはコピー 代入演算子を生成します。
  4. 独自に宣言しない限り、コンパイラはデストラクタを生成します。

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


C ++ 11

C ++ 11は、C ++ 14にも当てはまる次のルールを追加します(towiのクレジット、このコメントを参照)

  • コンパイラは、次の場合 に移動 コンストラクタを生成します
    • ユーザーが宣言したコピー コンストラクターはなく、
    • ユーザーが宣言したコピー 代入演算子はなく、
    • ユーザーが宣言したムーブ 代入演算子はなく、
    • ユーザーが宣言したデストラクタはありません、
    • dとマークされていません。delete
    • そして、すべてのメンバーとベースは移動可能です。
  • 同様に、ムーブ 代入演算子の場合、次の場合に生成されます。
    • ユーザーが宣言したコピー コンストラクターはなく、
    • ユーザーが宣言したコピー 代入演算子はなく、
    • ユーザーが宣言した移動 コンストラクターはなく、
    • ユーザーが宣言したデストラクタはありません、
    • dとマークされていません。delete
    • そして、すべてのメンバーとベースは移動可能です。

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

上記の内容をより簡単に理解するには、次のようにします。

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 ++の初心者の場合は、 MartinhoFernandesによって書かれた記事からの5つの別名TheRuleOfZeroのいずれも実装する必要のない設計を検討してください。

于 2010-09-17T09:50:27.673 に答える
2

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

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

'created'が'defined'を意味する場合、C++標準の重要な部分は次のとおりです。

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

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

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

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

于 2010-09-17T10:00:01.103 に答える
2

C ++17N4659標準ドラフト

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 6.1「宣言と定義」には、それらすべてを要約している可能性が高いメモがあります。

3 [注:状況によっては、C ++実装は、デフォルトコンストラクター(15.1)、コピーコンストラクター(15.8)、ムーブコンストラクター(15.8)、コピー代入演算子(15.8)、ムーブ代入演算子(15.8)、またはデストラクタ( 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ように説明されているようにそれを使用することです。クラスの関数宣言の後の「デフォルト」とはどういう意味ですか?

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

#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
于 2018-11-15T10:08:18.917 に答える
1

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

  • デフォルトのコンストラクタ
  • コピーコンストラクタ
  • コピー演算子(代入)
  • デストラクタ

メンバーのタイプと、リストされているメンバー関数を自分で提供する場合、それらがすべて生成されるわけではありません。

于 2010-09-17T09:51:48.713 に答える
0

他の回答は、何が作成されたか、そしてコンパイラは使用された場合にのみそれらを生成する可能性があることを示しています。

私の懸念は、それがすべてのクラス用に作成されているかどうかです...

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

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

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

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

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

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

于 2010-09-17T10:05:21.300 に答える