8

コンストラクターを完全に連鎖させずに、基本クラスから派生クラス名を出力するにはどうすればよいですか。言い換えれば、各派生クラスにコードを追加せずに、基本クラスから厳密にこれを行うことは可能ですか?

これは私が得たものの例であり、コンストラクターの連鎖を取り除く方法があれば。

編集: 理想的には、すべての派生クラスを編集することなく、基本クラスに追加するものを探しています。現時点では、私の実際のコードには最大17のクラスがあり(さらに必要です)、基本クラスから直接ジョブを実行できるものが理想的です。コンパイラ固有(g ++またはclang)であっても。

#include <iostream>

class Base {
public:
    Base(std::string id) {
            std::cout<<"Creating "<<id<<std::endl;
    }
};

class Child : Base {
public:
    Child(std::string id) : Base(id) {}
    Child() : Base(typeid(this).name()) {}
};

class GrandChild : Child {
public:
    GrandChild(std::string id) : Child(id) {}
    GrandChild() : Child(typeid(this).name()) {}
};

class GrandGrandChild : GrandChild {
public:
    GrandGrandChild(std::string id) : GrandChild(id) {}
    GrandGrandChild() : GrandChild(typeid(this).name()) {}
};



int main() {
    GrandGrandChild *A = new GrandGrandChild();
    GrandChild *B = new GrandChild();
    Child *C = new Child();

    return 0;
}

どの印刷物:

Creating GrandGrandChild
Creating GrandChild
Creating Child

しかし、コンパイルされた追加のプレフィックスがあります。

4

3 に答える 3

10

残念ながら、簡単な解決策はありません。

問題は、ポリモーフィックオブジェクトの構築が非常に複雑であるということです。クラスのBaseサブパートを構築している時点では、静止画を構築しています。メンバーにアクセスしようとしても意味がないため、まだ構築されていません。 )。ChildBaseChildChild

そのため、動的情報(RTTIまたはランタイムタイプ情報として知られる)を取得するすべての方法は、そのような間違いを防ぐために自発的にロックダウンされます。

対称的な理由から、デストラクタでも同じことが起こります。


これで、コンストラクタとデストラクタのみがロックダウンされるため、他のすべての場合にインスタンスの動的型の実際のname()名前を喜んで返すメソッドを完全に作成できます。

class Base {
public:
    std::string name() const { return typeid(*this).name(); }
};

それは動作します...コンストラクタまたはデストラクタから呼び出さない限り、静的型を報告します。

さて、「奇妙な」出力に関しては、各実装(コンパイラー)はここで独自の出力を提供することができます(そして、それらは異なるタイプで異なる必要はありません、クレイジーです!)。gccまたはclangを使用しているようです。

このような出力を解釈するためのデマングラーがあります。または、プログラムが十分に単純で、そのインターフェイスが怖い場合は、手動で解析して問題を取り除くことができます。クラスの名前は完全に表示される必要があります。その前には、意味のないものがいくつかあります(基本的に名前空間と数字)。

于 2012-06-05T11:15:54.513 に答える
0

これはデバッグ用であることを示しているので、仮想継承に依存して、すべての中間派生クラスに名前が渡されるのを避け、代わりに直接に渡すことができBaseます。また、Baseテンプレートコンストラクターを使用して、派生クラスの内容を単純化するように変更することもできます。

class Base {
public:
    template <typename DERIVED>
    Base (DERIVED *d) {
        std::cout << "Creating " << typeid(*d).name() << std::endl;
    }
};

class Child : virtual public Base {
public:
    Child () : Base(this) {}
};

class GrandChild : public Child, virtual public Base {
    GrandChild () : Base(this) {}
}

class GrandGrandChild : public GrandChild, virtual public Base {
    GrandGrandChild () : Base(this) {}
}
于 2012-06-05T10:03:43.207 に答える
0

各コンストラクターから呼び出す必要のある初期化関数を提供できます。

class Base {
protected:
  Base() { init(typeid(this).name()); }
  void init(std::string id) {
    std::cout<<"Creating "<<id<<std::endl;
  }
};

どういうわけか、後続のinitが前のinitの変更に安全に取って代わることを確認する必要があります。

Creating P4Base
Creating P5Child
Creating P10GrandChild
Creating P15GrandGrandChild
Creating P4Base
Creating P5Child
Creating P10GrandChild
Creating P4Base
Creating P5Child

純粋にデバッグ目的で使用するつもりです。そのため、基本クラスに何かを投入すると便利です。

デバッグ出力を出力するためにコードにマクロを追加することを検討しましたか?

#ifdef DEBUG
  #define PRINT_CLASSNAME std::cout<<"Creating "<<id<<std::endl;
#else
  #define PRINT_CLASSNAME ;
#endif

コンストラクターに一度追加する必要がありますが、(一時的に)無効にしたい場合は、定義を解除するだけですか?

于 2012-06-05T10:15:02.737 に答える