質問

C++ には仮想コンストラクターがないのはなぜですか?

正しい解決策はありません

他のヒント

馬の口の:)からそれを聞きます。

ビャーネ・ストロヴストルップのC ++スタイルとテクニックよくある質問から なぜ我々は仮想コンストラクタを持っていない?

  

仮想呼び出しは、作業が部分的に与えられた成し遂げるためのメカニズムであります   情報。具体的には、「仮想のは」私たちは、関数を呼び出すことができます   唯一任意のインタフェースではなく、オブジェクトの正確な型を知ります。に   あなたは完全な情報を必要とするオブジェクトを作成します。具体的には、あなた   あなたが作成したいの正確な型を知っている必要があります。その結果、   「コンストラクタの呼び出し」仮想することはできません。

FAQエントリは、仮想コンストラクタなしで、この目的を達成するための方法のためのコードを与えることになっています。

仮想関数は、基本的に多型動作を提供します。すなわち、のオブジェクトの種類の代わりに適切なある動作を提供し、あなたがそのダイナミック型静的とは異なる(コンパイル時)を入力し、それを参照しているオブジェクトを操作するとき、ありますオブジェクトの静的な型

今コンストラクタに行動のその種を適用しよう。あなたがオブジェクトを作成する場合、静的な型は常に以来、実際のオブジェクト型と同じです。

  

オブジェクトを構築するためには、コンストラクタは、[...]さらに[...]あなたはコンストラクタへのポインタを持つことができない、それが作成するオブジェクトの正確な型を必要とします。

(Bjarne Stroustup(P424ザC ++プログラミング言語SE))

などコンストラクタは、クラスを表すオブジェクトの仮想メソッドであるSmalltalkのやPython、(のようなオブジェクト指向言語とは異なり意味しているあなたはGoFのを必要としない<のhref = "http://c2.com/cgi /ウィキ?AbstractFactoryPattern」のrel = 『noreferrer』> Abstract Factoryパターンに、あなたは)周りのクラスを表す代わりに、あなた自身を作ったオブジェクトを渡すことができますよう、C ++はクラスベースの言語であり、いずれかを表すオブジェクトを持っていません言語の構文の。あなたはそれで仮想メソッドを呼び出すことはできませんので、このクラスは、実行時にオブジェクトとして存在していません。

私が見てきたすべての大規模なC ++プロジェクトは、抽象工場や反射のいくつかのフォームを実装してしまったものの、

これは、「あなたが使用していないもののために払っていない」哲学とフィットします。

私は考えることができる二つの理由ます:

技術理由

オブジェクトは、仮想テーブルを使用してディスパッチするコンストラクタのコンストラクタends.In注文後、仮想テーブルへのポインタと、既存のオブジェクトが存在しなければならない存在が、どのように仮想テーブルへのポインタが存在することができますオブジェクトがまだ存在しない場合は? :)

ロジックの理由

あなたはやや多型の行動を宣言したいとき

あなたはvirtualキーワードを使用します。しかし、コンストラクタでの多型は何もありません、C ++のコンストラクタジョブは単にメモリ上のオブジェクトのデータを置くことです。仮想テーブル(および一般的な多型)は、全ての多行動についてかなり多型データにしているので、仮想コンストラクタを宣言しても意味がありません。

私たちは、それだけで、コンストラクタではない: - )

struct A {
  virtual ~A() {}
  virtual A * Clone() { return new A; }
};

struct B : public A {
  virtual A * Clone() { return new B; }
};

int main() {

   A * a1 = new B;
   A * a2 = a1->Clone();    // virtual construction
   delete a2;
   delete a1;
}

セマンティック理由はさておき、何のvtableは、このように仮想指定が無駄なって、オブジェクトが構築されるまで存在しない。

まとめ:C++ 標準 できた 「仮想コンストラクター」の表記法と動作を指定します。これはかなり直観的で、コンパイラーがサポートするのはそれほど難しくありませんが、特に 機能性 を使用してすでにクリーンに実装できます create() / clone() (以下を参照してください)?これは、パイプラインにある他の多くの言語提案ほど有用ではありません。

議論

「仮想コンストラクター」メカニズムを仮定してみましょう。

Base* p = new Derived(...);
Base* p2 = new p->Base();  // possible syntax???

上記の最初の行は、 Derived 反対しますので、 *pの仮想ディスパッチ テーブルは、2 行目で使用する「仮想コンストラクター」を合理的に提供できます。(このページには次のような回答が数十件あります) 「オブジェクトはまだ存在しないため、仮想構築は不可能です」 構築されるオブジェクトに不必要に近視眼的に焦点を当てています。)

2行目は次の表記を前提としています。 new p->Base() 動的割り当てと別のデフォルト構築を要求する Derived 物体。

ノート:

  • コンパイラはコンストラクタを呼び出す前にメモリ割り当てを調整する必要があります - コンストラクターは通常サポートします 自動 (非公式には「スタック」) 割り当て、 静的 (グローバル/名前空間スコープおよびクラス/関数-の場合)static オブジェクト)、および 動的 (非公式に「ヒープ」) new 使用されている

    • 構築されるオブジェクトのサイズ p->Base() 通常はコンパイル時に知ることができないため、 動的割り当ては意味のある唯一のアプローチです

      • 実行時に指定された量のメモリをスタック上に割り当てることができます。 GCC の可変長配列拡張, alloca() - しかし、重大な非効率性と複雑さをもたらします(例: ここ そして ここ それぞれ)
  • 動的割り当ての場合は しなければならない ポインタを返すので、メモリを delete後で。

  • 仮定された表記法は明示的にリストします new 動的割り当てとポインター結果の型を強調するため。

コンパイラは次のことを行う必要があります。

  • メモリの量を調べる Derived 暗黙的なメソッドを呼び出すことで必要になります virtual sizeof 機能、またはそのような情報を RTTI 経由で利用できるようにする
  • 電話 operator new(size_t) メモリを割り当てる
  • 呼び出す Derived() 配置あり new.

または

  • 動的割り当てと構築を組み合わせた関数用の追加の vtable エントリを作成する

したがって、仮想コンストラクターを指定して実装することは克服できないことではないようですが、100 万ドル規模の質問は次のとおりです。既存の C++ 言語機能を使用して実現できることよりも優れていることは何でしょうか...?個人的には、 以下の解決策に利点はありません。


`clone()` と `create()`

C++ FAQ には「仮想コンストラクター」のイディオムが記載されています, 、を含む virtual create() そして clone() 新しい動的に割り当てられたオブジェクトをデフォルト構築またはコピー構築するメソッド:

class Shape {
  public:
    virtual ~Shape() { } // A virtual destructor
    virtual void draw() = 0; // A pure virtual function
    virtual void move() = 0;
    // ...
    virtual Shape* clone() const = 0; // Uses the copy constructor
    virtual Shape* create() const = 0; // Uses the default constructor
};
class Circle : public Shape {
  public:
    Circle* clone() const; // Covariant Return Types; see below
    Circle* create() const; // Covariant Return Types; see below
    // ...
};
Circle* Circle::clone() const { return new Circle(*this); }
Circle* Circle::create() const { return new Circle(); }

変更またはオーバーロードすることも可能です create() 引数を受け入れますが、基本クラス/インターフェイスと一致します。 virtual 関数のシグネチャ、オーバーライドする引数は、基本クラスのオーバーロードの 1 つと正確に一致する必要があります。これらの明示的なユーザー提供機能を使用すると、ロギング、インストルメンテーションの追加、メモリ割り当ての変更などが簡単に行えます。

あなたは一例であり、それは@stefanの答えでは許可されていない理由に技術的な理由を見つけることができます。今、私によると、この質問への論理的な答えはあります:

virtualキーワードの主な用途は、我々は、基本クラスのポインタがポイントするオブジェクトの種類がわからないとき多型の動作を可能にすることです。

しかし、これを考えるあなたは、ポインタを必要とする仮想機能を使用するための、より原始的な方法です。そして、ポインタは何が必要なのでしょうか?を指すようにオブジェクト! (プログラムの正しい実行のためのケースを考慮して)

ですから、私たちは基本的に既にメモリのどこかに存在するオブジェクト必要が私たちのポインタが正しく、そのオブジェクトを指すことができますように(私たちはメモリが割り当てられたかに関係していないが、それはコンパイル時またはいずれかの実行時にあってもよいです)。

>そのコンストラクタは、そのインスタンス自体で自動的に呼び出されます。

- !

さて、指摘されるクラスのオブジェクトは、いくつかのメモリを割り当てられている瞬間についての状況を考えます

だから我々はあなたがポリモーフィック振る舞いを使用したいのいずれかの場合には、当社のコンストラクタは、すでに使用のための私たちのオブジェクトの準備ができて作り、実行されていたので、我々は実際に、コンストラクタは仮想であることを心配する必要はありませんことを見ることができます!

仮想コンストラクタの概念は、オブジェクトの型がオブジェクトを作成するための前提条件であるだけでなくので、中に入れることができませんが、そのcompletlyオーバー支配ません。

GOFの「ファクトリメソッド」デザインパターンは、特定の設計状況でhandlyある仮想コンストラクタの「コンセプト」、を利用しています。

C ++での仮想関数は、実行時のポリモーフィズムの実装であり、それらは、関数のオーバーライドを行います。あなたは動的挙動を必要とするとき、一般的にvirtualキーワードはC ++で使用されています。これは、オブジェクトが存在する場合にのみ動作します。一方、コンストラクタは、オブジェクトを作成するために使用されています。コンストラクタは、オブジェクトの作成時に呼び出されます。

あなたはvirtualとしてコンストラクタを作成するのであれば、

、仮想キーワードの定義に従って、それを使用する既存のオブジェクトを持っているはずですが、コンストラクタは、オブジェクトを作成するために使用されているので、この場合は存在しません。だから、仮想としてコンストラクタを使用しないでください。

だから、我々はエラーをスロー仮想コンストラクタコンパイラを宣言しようとした場合:

  

コンストラクタが仮想宣言することはできません。

人々はこのような質問をすると、

、私は自分自身に考えたい「これは実際に可能であった場合に何が起こりますか?」私は本当にこれが意味するだろうかわからないが、私はそれが作成されたオブジェクトの動的な型に基づくコンストラクタの実装を上書きすることができることとは何かを持っているだろうと思います。

私はこの潜在的な問題の数を参照してください。一つには、派生クラスは、完全に仮想コンストラクタが呼び出された時に構築、その実装の潜在的な問題があることはありません。

第二に、多重継承の場合に何が起こるのでしょうか?あなたの仮想コンストラクタは、あなたが1と呼ばれていたノウハウのいくつかの方法を持っている必要があるだろう、おそらく複数回呼び出されます。

第三に、一般的に工事の時に話して、オブジェクトが仮想テーブルが完全に構築されていない、これは、オブジェクトの動的な型があることという事実のためにできるように、言語仕様に大きな変更を必要とする意味します構築時に知られています。これは、その後、基底クラスのコンストラクタは、そうでないかもしれない完全に構築動的なクラス型で、建設時に他の仮想関数を呼び出すことができるようになります。

他の誰かが指摘したように、

最後に、あなたは基本的にはどうなるの仮想コンストラクタと同じことを行う静的な「を作成」または「初期化」型の関数を使用して、仮想コンストラクタのようなものを実装することができます。

仮想関数は、オブジェクトのタイプに基づいて機能を呼び出すために使用されるポインタはなく、ポインタ自体の種類によって指さ。しかし、コンストラクタが「呼び出され」ではありません。オブジェクトが宣言されたときに一度だけ呼ばれます。だから、コンストラクタはC ++で仮想にすることはできません。

あなたはどちらかあなたのコンストラクタ内の仮想関数を呼び出すべきではありません。参照してください: http://www.artima.com/cppsource/nevercall.htmlする

また、私はあなたが本当に仮想コンストラクタが必要であることはよく分かりません。あなたはそれなしで多型建設を達成することができます。あなたが必要なパラメータに応じて、あなたのオブジェクトを作成する関数を書くことができます。

の仮想テーブル(vtableの)は、各クラスは、1つまたは複数の「仮想関数」を有するために作られています。オブジェクトは、そのようなクラスから作成されるたびに、対応する仮想テーブルのベースを指す「仮想ポインタ」を含みます。仮想関数呼び出しがあるたびに、vtableのは、関数のアドレスを解決するために使用されます。     クラスのコンストラクタが実行されたときにメモリにはvtableのがないのでコンストラクタは、仮想ことができない、まだ定義された仮想ポインタを意味します。したがって、コンストラクタは常に非仮想である必要があります。

カントは、我々は単に私たちは、コンストラクタを継承することはできません。..のようなそれを言います。だから、仮想の多型を提供するので、それらを仮想宣言しない点はありません。

あなたは、派生クラスのオブジェクトへのベースクラスのポインタを持っている場合、

仮想メカにのみ機能します。建設は、それが基本的に基本クラスを派生するために、基本クラスのコンストラクタの呼び出しのための独自のルールのしています。どのように仮想コンストラクタは便利かと呼ばれるだろうか?私は他の言語は何をすべきかわからないが、私は仮想コンストラクタが有用かさえ実装することができるか見ることができません。建設には何の意味と建設を作るための仮想メカも多形性行動の仕組みを提供する作成されているためにvtableの構造に対して行われている必要があるために行われている必要があります。

非常に基本的な理由があります:。コンストラクタが効果的に静的関数であり、C ++で静的な関数は仮想になることはできません。

あなたがC ++で多くの経験を持っている場合は、すべての静的&メンバー関数の違いを知っています。静的関数は、クラスではなく、オブジェクト(インスタンス)に関連付けられているので、彼らは「この」ポインタが表示されません。唯一のメンバ関数は、実際に各オブジェクトのデータメンバであるワーク・「仮想」になり、関数ポインタのvtable-隠されたテーブルので、仮想することができます。

さて、コンストラクタの仕事は何ですか?それは彼らが割り当てられているとして、「T」は、コンストラクタTのオブジェクトを初期化名 - です。これは自動的にメンバ関数であること排除します!オブジェクトは、それは、このように「この」ポインタとvtableのを持って前に存在している必要があります。それは場合でも、通常の関数として扱わ言語コンストラクタは(それは、関連する理由のために、私は入れないだろうしない)、彼らは静的メンバ関数でなければならないだろうということを意味します。

これを見るための素晴らしい方法は、特にファクトリ関数、「工場」のパターンを見ることです。彼らはあなたが後にしている何を、あなたはクラスTは、ファクトリメソッドを持っている場合、それは常にSTATICであることがわかります。それはである必要があります。

C++ 仮想コンストラクターは使用できません。たとえば、コンストラクターを仮想としてマークすることはできません。このコードを試してください。

#include<iostream.h>
using namespace std;
class aClass
{
    public:
        virtual aClass()
        {   
        }  
};
int main()
{
    aClass a; 
}

エラーが発生します。このコードはコンストラクターを仮想として宣言しようとしています。ここで、仮想キーワードを使用する理由を理解してみましょう。Virtual キーワードは、実行時のポリモーフィズムを提供するために使用されます。たとえば、このコードを試してください。

#include<iostream.h>
using namespace std;
class aClass
{
    public:
        aClass()
        {
            cout<<"aClass contructor\n";
        }
        ~aClass()
        {
            cout<<"aClass destructor\n";
        }

};
class anotherClass:public aClass
{

    public:
        anotherClass()
        {
            cout<<"anotherClass Constructor\n";
        }
        ~anotherClass()
        {
            cout<<"anotherClass destructor\n";
        }

};
int main()
{
    aClass* a;
    a=new anotherClass;
    delete a;   
    getchar(); 
}

メインで a=new anotherClass; メモリを割り当てます anotherClass ポインタで a の型として宣言される aClassこれにより、両方のコンストラクターが発生します ( aClass そして anotherClass) を自動的に呼び出します。そのため、コンストラクターを仮想としてマークする必要はありません。オブジェクトが作成されるときは、作成のチェーン (つまり、最初に基本クラス、次に派生クラス) に従う必要があるためです。しかし、削除しようとすると、 delete a; 基本デストラクタのみを呼び出すことになるため、virtual キーワードを使用してデストラクタを処理する必要があります。 したがって、仮想コンストラクターは不可能ですが、仮想デストラクターは可能です。ありがとう

あなたはコンストラクタがどのように機能するかについて、論理的に考え、どのような仮想関数の意味/使用量はC ++である場合は、

あなたは仮想コンストラクタはC ++に意味がないことを理解するであろう。 C ++での仮想何かを宣言すると、反対が作成されたときしかし、コンストラクタが呼び出され、現在のクラスのサブクラスで上書きすることができることを意味し、あなたがクラスのサブクラスを作成することはできませんその時に、あなたがでなければなりません仮想コンストラクタを宣言する必要があることはないので、クラスを作成します。

そしてもう一つの理由は、コンストラクタは、そのクラス名と同じ名前を持っているし、我々は仮想としてコンストラクタを宣言した場合、それは同じ名前を持つその派生クラスで再定義する必要がありますが、次の2つの同じ名前を持つことはできませんクラス。だから、仮想コンストラクタを持ってすることはできません。

  1. コンストラクターが呼び出されるとき、その時点までオブジェクトは作成されませんが、作成されるオブジェクトの種類はまだわかっています。 特定のコンストラクター オブジェクトが属するクラスの関数はすでに呼び出されています。

    Virtual 機能に関連付けられたキーワードは、 特定のオブジェクトタイプの関数 呼ばれることになる。

    したがって、私の考えでは、オブジェクトが作成される目的のコンストラクターがすでに呼び出されており、コンストラクターを仮想化することは単なる冗長な作業であるため、仮想コンストラクターを作成する必要はありません。 オブジェクト固有のコンストラクター はすでに呼び出されており、これは呼び出しと同じです クラス固有の関数 これは、virtual キーワードによって実現されます。

    ただし、内部実装では vptr および vtable 関連の理由により仮想コンストラクターは許可されません。


  1. もう 1 つの理由は、C++ は静的に型指定された言語であり、コンパイル時に変数の型を知る必要があることです。

    コンパイラは、オブジェクトを作成するためにクラスの型を認識している必要があります。作成されるオブジェクトのタイプはコンパイル時に決定されます。

    コンストラクターを仮想化すると、コンパイル時にオブジェクトの型を知る必要がなくなることを意味します (それが仮想関数が提供するものです)。実際のオブジェクトを知る必要はなく、実際のオブジェクトを指すためのベース ポインターが必要なだけです (オブジェクトの型を知らなくても、指すオブジェクトの仮想関数を呼び出します)。また、コンパイル時にオブジェクトの型がわからない場合は、その場合、それは静的型付け言語に反します。したがって、実行時のポリモーフィズムは実現できません。

    したがって、コンパイル時にオブジェクトの型が分からない限り、Constructor は呼び出されません。したがって、仮想コンストラクターを作成するというアイデアは失敗します。

Vpointerは、オブジェクトの作成時に作成されます。 vpointerは文句を言わないオブジェクトを作成する前に存在しています。そのコンストラクタは、仮想作るのも意味がありません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top