2

C++

Base基本クラスと派生クラスが与えられた場合、のDerivedコンストラクターによって最初に構築されるDerivedのはBaseサブオブジェクトです。これはサブオブジェクトと呼ばれるため、オブジェクトでドット演算子を使用することにより、他のメンバー オブジェクトと同様にクライアント コードからアクセスできると想定しましたDerivedDerivedまた、 によって の実装コードからアクセスできると仮定しましたthis->Base。セミコロンが後に続く、既に初期化されているオブジェクトの名前だけで構成されるステートメントは、コンパイルする必要がありますが、効果はありません。そのロジックに従って、Derivedobjectを指定して、次のmyderivedことを試しました:myderived.Base;クライアント コードthis->Base;内およびDerivedの実装内で、どちらのステートメントもコンパイルされません。

なんで?Baseは、それ自体がBaseクラスの名前であり、オブジェクトの名前ではないことを知っていBaseます。しかし、 (クライアント コード) または(実装コード) プレフィックスBaseによって修飾されたものは基本サブオブジェクトを参照すると思いました。以下のコードを参照してください。これは (コメントアウトされたコードは別として) VC12 および g++ 4.8 で動作します。extendsとの定義はデータ メンバーを宣言するので、私のオブジェクトには 2 つが含まれている必要がありますmyderived.this->BaseBaseDerivedDerivedBaseDerivedBasemembaseDerivedBaseオブジェクト。コンパイルの成功がコンパイラ標準の不適合の結果ではないと仮定すると、2 つの異なるオブジェクトのintメンバーに異なる値を示すコンソール出力 (コメント内) は、 inの ctor 初期化子が継承されたサブオブジェクトは、宣言されたデータ メンバー オブジェクトを参照します。の ctor 初期化子では、オブジェクトやクラスだけでなく、継承されたサブオブジェクトを具体的に参照します。nBaseDerivedBaseBasemembaseDerivedBaseBaseBase

#include <iostream>

struct Base {
    Base(int par) : n(par) {}
    void foo() { std::cout << "Base: " << n << std::endl; }
    int n;
};

struct Derived : Base {
    Derived() : Base(2), membase(3) {}
    Base membase;
    void foo() { std::cout << "Derived: " << n << std::endl; }

    // void g() { this->Base; } // Error in VC12 & g++ 4.8
    // ^ VC12: error C2273: 'function-style cast' : illegal as
    // right side of '->' operator
};

int main() {
    Derived myderived;

    // myderived.Base; //Error in VC12 & g++ 4.8
    // ^ VC12: error C2274: 'function-style cast' : illegal as
    // right side of '.' operator

    myderived.foo();           // OUTPUT: "Derived: 2"
    myderived.Base::foo();     // OUTPUT: "Base: 2"
    myderived.membase.foo();   // OUTPUT: "Base: 3"
}
  1. 繰り返しますが、継承されたサブオブジェクトを一意に参照してコンパイルするべきではありませんmyderived.Base;か?this->Base;Base

  2. は、サブオブジェクト、クラス、または何かを参照していBaseますか?myderived.Basethis->BaseBaseBase

  3. 一般に、継承された基本サブオブジェクトは派生クラスのデータ メンバーと見なされますか?

  4. の観点から、Derivedのコンストラクター初期化子のBaseコンテキスト内で継承されたサブオブジェクトのみを参照し、の ctor 初期化子の外側のクラスのみを参照しますか?DerivedBaseDerived

  5. の実装コードとクライアント コードで「Baseオブジェクトの継承されたサブオブジェクト」Derivedを表現するにはどうすればよいでしょうか。BaseDerivedDerived

  6. でスコープ解決演算子を使用すると、myderived.Base::foo()foo()のメソッドでありBase、VC12 および g++ 4.8 でコンパイルされます。とドット演算子で修飾されているため、これBaseは のデータ メンバであることを意味しますか? もしそうなら、それはクラスですか、それともサブオブジェクトですか?myderivedmyderivedBaseBaseBase

  7. しかしmyderived.Base.foo()、コンパイルされません。オブジェクトのメンバーのAFAIKアクセスは、オブジェクト名とドット演算子によってクライアントコードで修飾されています。オブジェクト名とドット演算子の代わりに、スコープ解決演算子によって修飾される 2 種類のものは、(a) 名前空間に属するものへの外部アクセス、および (b) 静的データ メンバーの名前とメンバー関数の名前です。クラス定義の外側で定義された定義の場合、 のBase前にあるはインスタンスではなくクラス::を参照します。これは in が名前空間であるか、クラスを参照していることを意味しますか?BaseBaseBasemyderived.Base

  8. もしそうなら、それは名前空間であるか、それとも::のメンバーが後に続くかどうかに基づいて条件付きのクラスを参照していますBaseか?

  9. #7 の答えが「はい」の場合、その理由は何ですか? 次のロジックとは矛盾しているように思われます: 名前空間で 1 つの変数を囲んでも、それ自体では、名前空間がその変数の型の他のインスタンスを囲んだり構築したりすることはできません。名前空間は、その型のインスタンスを 1 つだけ所有します。つまり、その型に含まれる変数です。静的データ メンバーのように、クラスの一部であるメンバーにも同じことが言えます。このクラスは、その型のインスタンスを 1 つだけ所有します。つまり、クラスに含まれる静的データ メンバーです。対照的に、クラスのインスタンスと同じ数の同じ名前の非静的データ メンバーがクラスに存在します。

  10. 指定されたメソッドh()およびオブジェクトは、VC12 および g++ 4.8 でコンパイルされます。さらに、g++ 4.8 では、 のように、そのステートメントで任意の数の余分な s を使用できます。このようなステートメントは、 が のメンバーであることを暗示しているようです。しかし、VC12 は. しかし、 objectが与えられた場合、VC12 は問題なくコンパイルされます。これは、VC12 がクラスをそれ自体のメンバーとして扱っても問題ないことも意味します。しかし、それは、以前の声明を編集できないことと矛盾しています。また、VC12 は、任意の数の余分なs (たとえば) を持つのバージョンをコンパイルできませんが、g++ はコンパイルできます。もしあれば、どのコンパイラが正しいですか?BaseDerivedmyderivedmyderived.Base::h();Base::myderived.Base::Base::h();BaseBaseerror C3083: '{ctor}': the symbol to the left of a '::' must be a typeBasemybasemybase.Base::h();mybase.Base::h();Base::mybase.Base::Base::h()

  11. いずれにせよ、それは名前空間またはクラスがそれ自体を含むことができるということですか? intグローバル変数が与えられた場合x、ステートメント::::x;(2 つのスコープ解決演算子を含む) はどちらのコンパイラでもコンパイルされないため、グローバル スコープにはグローバル スコープが含まれていないと想定しています。

4

1 に答える 1

9
  1. いいえ、サブオブジェクトBaseとは別の名前のメンバーを持つことができます。Baseパン::クチュエータは、メンバー オブジェクト名を無視するように名前解決を制限します。
  2. #1を参照してください。通常、答えはノーです。なぜなら、メンバーのエイリアスを意図的にベースにするのは気が狂ってしまうからです。ただし、クラスがそのベースの名前を認識していない可能性があるテンプレートで発生する可能性があります。
  3. いいえ。メンバー サブオブジェクトとベース サブオブジェクトはどちらもサブオブジェクトですが、アクセス方法が異なります。
  4. 常にクラスを参照し、名前Base自体は から継承されBaseます。クレイジーなメンバー エイリアスがある場合はBase、名前空間修飾 ID などへの他の参照を使用する必要があります。
  5. static_cast< Base & >( derived_obj ).
  6. いいえ、メンバーが内部で検索され、関数呼び出し演算子が適用される::よりも優先されます。ただし、 は部分式を生成する演算子ではないため、括弧を使用することはできません。これが、私がそれを句読点と呼ぶことを好む理由です。.Base::foomyderived(Base::foo)::
  7. #6を参照してください。Base はbeforemyderived.Baseとグループ化されるため、それ自体では何もありません。::.
  8. 右。クラスは名前空間ではないことに注意してください。これらは異なるものであり、どちらもたまたま同じスコープ表記で機能します。
  9. これは、別の言語に適用される可能性のある用語で C++ を説明しているようです。たとえばJavaでは、クラスは独自のデータメンバーを持つ一種のオブジェクトです。C++ ではstatic、クラス メンバーと名前空間メンバーは完全に別のオブジェクトであり、どこにでも定義できます。
  10. Base::Base::Base::クラスの名前が member であるかのようにそれ自体に注入されるため、機能しtypedefます。VC はおそらくエラーを起こし、それをコンストラクターへの参照として解釈しています。仕様によると、特別な typedef (注入されたクラス名と呼ばれる) は、特別な状況下でコンストラクターを参照しますが、スコープ演算子の前はそのような場合ではありません
  11. すべてのクラスには、それ自体に対する暗黙の が含まれていtypedefます。繰り返しますが、名前空間とクラスはまったく別のものです。

    接頭辞::自体はグローバル名前空間の名前ではなく、名前がないことを補うための文法上の特殊なケースです。同様に、良くも悪くも、宣言することはできません

    namespace global = :: ; // error: :: alone does not name anything.
    
于 2013-11-27T00:18:19.100 に答える