0

基本コンストラクターによって呼び出された関数内から派生クラスの関数を呼び出すことができるかどうか疑問に思っています (括弧内のコードが実行されたときに既に作成されているはずではありませんか?)

#pragma once
class ClassA
{
public:
 ClassA(void);
 virtual ~ClassA(void);

 void Init();

protected:
 short m_a;
 short m_b;

 virtual void SetNumbers(short s);
};

include "ClassA.h"
#include <iostream>


ClassA::ClassA(void) : m_a(0), m_b(0)
{
Init();
}


ClassA::~ClassA(void)
{
}

void ClassA::SetNumbers(short s)
{
 std::cout << "In ClassA::SetNumbers()\n";
 m_a = s;
 m_b = s;
}

void ClassA::Init()
{
 this->SetNumbers(2);
}

#pragma once
#include "ClassA.h"
class ClassB : public ClassA
{
public:
 ClassB(void);
 virtual ~ClassB(void);

 virtual void SetNumbers(short);

 int x;
};

#include "ClassB.h"
#include <iostream>


ClassB::ClassB(void)
{
}


ClassB::~ClassB(void)
{
}

void ClassB::SetNumbers(short s)
{
 std::cout << "In ClassB::SetNumbers()\n";

 m_a = ++s;
 m_b = s;

 ClassA::SetNumbers(s);
}

それを行う方法の提案はありますか? ...

前もって感謝します :)...

4

5 に答える 5

2

基本クラスが構築されている間は、派生クラスの構築が始まっていないという単純な論理的理由により、これを行うことはできません。(まだ) 存在しないオブジェクトに対してメンバー関数を呼び出すことはできません。

実際には、SetNumbers初期化される前に派生クラスのメンバー変数を呼び出して代入できたとしても、最終的に初期化されると確実に上書きされます。私たちは定義された行動の外にあるので、これについて推論するのは少し無意味であることを認めます.

于 2010-08-18T19:03:45.553 に答える
2

B のすべての部分 (ベースとして A から始まる) は、B のコンストラクターが呼び出される前に構築されます。そのため、SetNumbersが呼び出されるまでに、B のどの部分も (A 部分を除いて) 構築されていません。これには v-table が含まれる可能性があるため、その呼び出しがどこに行くのかを知る方法はありません。

もちろん、これには簡単な解決策があります: B のコンストラクター内から B::SetNumber() を呼び出します (つまり、結局、B のコンストラクターの目的です)。

于 2010-08-18T19:04:26.640 に答える
1

いいえ、ごめんなさい。:(1つまたは2つのC++コンパイラでコンパイルされる可能性がありますが、お勧めしません。C++ FAQ Liteセクション10.7から:

[10.7]コンストラクターでthisポインターを使用する必要がありますか?

[...をちょきちょきと切る...]

決して機能しないものがあります。 コンストラクター(またはコンストラクターから呼び出された関数)の{body}は、派生クラスでオーバーライドされた仮想メンバー関数を呼び出しても、派生クラスに到達できません。派生クラスのオーバーライドされた関数に到達することが目標である場合、必要なものを取得できません。仮想メンバー関数の呼び出し方法に関係なく、派生クラスのオーバーライドに到達しないことに注意してください。明示的にthisポインターを使用し(たとえば、this-> method())、暗黙的にthisポインターを使用します(たとえば、method( ))、またはこのオブジェクトの仮想メンバー関数を呼び出す他の関数を呼び出すこともできます。肝心なのはこれです:呼び出し元が派生クラスのオブジェクトを構築している場合でも、基本クラスのコンストラクター中に、オブジェクトはまだその派生クラスのものではありません。あなたは警告されました。

注:強調鉱山。

リンクで詳細

于 2010-08-18T19:09:18.497 に答える
1

これを行うことができるのは、それ自体がパラメーター化されたテンプレートから何かが派生した場合のみです。

template<typename T> class base
{
  T* down_cast() throw()
  {
    return static_cast<Derived*>(this);
  }
  const T* down_cast() const throw()
  {
    return static_cast<const Derived*>(this);
  }
public:
  base()
  {
    down_cast()->doSomething();
  }
 /* … */
};

class derived : private base<derived>
{
public:
  void doSomething()
  {
  }
};

doSomethingはパブリックであり、仮想ではないことに注意してください。

派生型が派生型であることがわかっているため、派生型に static_cast できます。

それ自体でパラメーター化されたベースから何かを導出することは、最高のときに行うのは奇妙なことです。Microsoft の ATL チームがそれを使用したとき、彼らは C++ コンパイラ チームにそれが有効かどうか尋ねましたが、テンプレートの構築は次のように名前に依存するため有効ですが、誰も確信が持てなかったと言われています。

まず、テンプレートは利用可能ですが、クラスでは使用されていません。次に、derived利用可能な名前。次に、 のレイアウトをインスタンス化しますbase<derived>— これには、メンバー変数と仮想関数の知識が必要ですが、それらが のレイアウト (ポインターと参照は問題ありません) の知識に依存しない限り、derivedこれはすべて問題ありません。次に のレイアウトを作成しderived、最後に のメンバー関数を作成します。derivedこれには、 のメンバー関数の作成が含まれる場合がありますbase<derived>base<derived>したがって、派生メンバー変数 (基本クラスには、それ自体から派生した型のメンバー変数を含めることはできません) または派生のレイアウトの知識を必要とする仮想関数が含まれていない限り、上記の危険な継承の一部を実際に実行できます。 .

これには、すでに base の一部であるため、構築中にderivedfromの非仮想パブリック メンバーを呼び出すことができることも含まれます。baseこれには強い制限があります。特に、のコンストラクdoSomething()ターで構築されたものに依存する場合、まだ構築さderivedれていないため機能しません。derived

さて、これは実際に良い考えですか?いいえ。

于 2010-08-18T19:27:06.257 に答える
0

単純な設計ソリューションは、継承の代わりに集計を使用することです。

于 2010-08-18T19:11:36.560 に答える