8

オブジェクトの構築を禁止するにはどうすればよいですか? = delete;関連するすべての特殊機能を次のようにマークします。

struct A
{
    A() = delete;
    A(A const &) = delete;
    A(A &&) = delete;
    void * operator new(std::size_t) = delete;
    void operator delete(void *) = delete;
};
A x{};
A y = {};
A * z = ::new A{};

LIVE EXAMPLE

しかしxyそして*zまだ存在することができます。何をすべきか?私は両方のケースに興味があります。静的/スタック割り当てとヒープ割り当て。

4

5 に答える 5

14

1 つのオプションは、クラスに純粋な仮想関数を与え、それを final としてマークすることです。

struct A final
{
  virtual void nonconstructible() = 0;
};

【実例】

于 2015-10-15T10:39:10.133 に答える
6
  1. staticメンバーだけが必要な場合は、namespace Aではなく. と記述しstruct Aます。後続のコードは構文的に類似しています。

  2. クラスのインスタンスが作成されないようにするには、クラスを抽象化します。(純粋仮想関数を 1 つ含めます)。しかし、これを行うと、クラスに v-table が導入されますが、これは望ましくない場合があります。

于 2015-10-15T10:37:52.483 に答える
6

クラスをインスタンス化できないようにしたい場合は、プライベート コンストラクターを宣言するだけです。

class NotInstantiable {
private:
    NotInstatiable();

public:
};

そして、それ以上定義しませんNotInstantiableprivate最初にコンストラクターが存在するだけでなく、コンストラクターの定義が提供されていないため、これをインスタンス化することはできません。

インスタンス化の 2 番目の障害NotInstantiableは、たとえば、この可能性を禁止します。これは、実際にはよく知られているパターンです。

class NotInstantiable {
private:
    NotInstantiable();

public:
    NotInstantiable* evil_method()
    {
        return new NotInstantiable(); // this will fail if there's no body of the constructor.
    }
};
于 2015-10-15T10:39:27.357 に答える
3

一般に、クラスのクライアント コードのインスタンス化を完全に防止するには、クラスを宣言してfinal

  • コンストラクタを非にするpublic、または

  • コンストラクターを削除し、クラスが集約ではないことを確認するか、または

  • 純粋仮想メンバー関数を追加して (例えば、デストラクタを純粋仮想にする)、クラスを抽象化します。

派生クラスの基本クラス サブオブジェクトのインスタンス化を防ぐためにfinal、非publicisの場合、および抽象クラスの場合、クラスの宣言が必要です。protected


インスタンス化を部分的に禁止するには、次のことができます。

  • デストラクタを non- にしpublicます。

これにより、自動変数と静的変数が防止されますが、動的割り当ては防止されませんnew

  • クラスの割り当て関数 ( operator new) を non-にしpublicます。

これによりnew、クライアント コードでの通常の -expression による動的割り当てが防止されますが、自動および静的変数、または他のオブジェクトのサブオブジェクトは提供されず::new、グローバル割り当て関数を使用する -expression による動的割り当ては防止されません。

new式を非常に複雑で実用的でないものにする余分な引数を持つ割り当て関数など、他の関連する手法もあります。これを一度使用して、特別なマクロを強制的に使用してオブジェクトを動的に割り当てました (例: shared-from-this class )。しかし、それは C++11 が引数の転送をサポートする前のことです。今日では、通常の関数で仕事をすることができ、そのような関数をfriendクラスにすることができます。


コードが少なくとも 1 つのバージョンの clang コンパイラでコンパイルされるという事実は、そのコンパイラ-std=gnu++1zのバグおよび/または言語拡張によるものです。

削除されたデフォルトのコンストラクターを呼び出すため、コードはコンパイルされません。また、MinGW g++ 5.1.0 などではコンパイルできません-std=gnu++1z

コードが少なくとも 1 つのバージョンの clang コンパイラでコンパイルされるという事実は、そのコンパイラ-std=gnu++1zのバグおよび/または言語拡張が原因である可能性があります。正しい動作が何であるかは不明です。

  • コードは clang および Visual C++ 2015 でコンパイルされますが、たとえば MinGW g++ 5.1.0 ではコンパイルされません-std=gnu++1z

  • 直観的にdeleteは、コードをコンパイルする必要がある場合は意味がありませんが、C++ では多くの無意味な構造が許可されています。

  • 問題は、クラスが集約であるかどうか (その場合、式は集約の初期化を実行します) です。これは、削除された既定のコンストラクターがユーザー提供のnewと見なすことができるかどうかにかかっています。また、ユーザーTartanLlamaがコメントで説明しているように、ユーザー提供の要件は次のとおりです。

C++11 §8.4.2/4

特別なメンバー関数は、ユーザーが宣言し、最初の宣言で明示的にデフォルト設定または削除されていない場合、ユーザー提供です。

つまりdelete、この質問の例のデフォルトのコンストラクターはそのコンストラクターを宣言していますが、それはユーザー提供ではないため (他のメンバーについても同様です)、クラスは集約です。

この文言に関して私が見つけることができる唯一の欠陥レポートはDR 1355ですが、これは単に「特別なメンバー」という言葉の使用に関する問題に関係しており、それらの言葉を削除することを提案しています。しかし、この質問によって実証された効果と、関数を最初の宣言でのみ削除できることの両方を考慮すると、言葉遣いは奇妙です。

まとめると、正式には C++11 の時点で (C++14 はチェックしていません)、コードはコンパイルされるはずです。しかし、これは文言が意図を反映していないため、標準の欠陥である可能性があります。また、MinGW g++ 5.1.0 はコードをコンパイルしないため、2015 年 10 月現在、コードのコンパイルに依存することはお勧めできません

于 2015-10-15T11:11:58.080 に答える