从基础 class 构造函数调用派生 class 的成员函数
Calling a member function of a derived class from the base class constructor
假设我有
struct C {
C() { init(); };
void init() { cout << "C" << endl; };
};
struct D : public C {
D() : C() { };
void init() { cout << "D" << endl; }
};
D();
为什么我打印 "C"?如何改变这种行为(并得到 "D")。
如果我两个都想要怎么办?
您只能从 C 构造函数中删除 init()
才能不打印 "C"。
要同时打印 "D",请在 D()
构造函数中添加 init()
。
如果在某些情况下你想要打印 "C" 或 "D" 而在某些情况下不要这样做
struct C {
C() { };
void init() { cout << "C" << endl; };
};
struct D : public C {
D() : C()
{
if(some condition)
C::init();
if(some condition)
init();
};
void init() { cout << "D" << endl; }
};
D();
why I get "C" printed?
C::init()
未声明为 virtual
,因此 D
无法覆盖它。但是,即使 C::init()
被声明为 virtual
,当 init
() 在 C
的构造函数。
C++ 在派生 classes 之前构造基 classes(并在基 classes 之前析构派生类)。所以 C
的构造函数在构造 D
之前运行(并且 C
的析构函数在 D
被破坏之后运行)。 constructed/destructed 对象的 VMT 根本不指向 D
的方法 table 当 C
被 constructed/destructed 时,它指向 C
的方法 table 而不是。
How can change this behaviour (and get "D").
您不能从基classconstructor/destructor内部调用派生虚方法。在这些阶段,VMT 不包含指向派生的 class 方法 table 的指针。
这里有一个非常基本的问题:您想在一个尚不存在的对象上调用派生 class 的成员函数。
请记住,对象是通过首先构造基础子对象然后构造派生对象来构造的。因此,即使您设法应用“聪明”的技巧来实际调用派生的 class' init
函数,一旦该函数尝试访问派生对象的任何数据成员,它会造成任意伤害。另一方面,只要您不依赖于构造函数尚未建立的任何不变量,就可以只访问基础对象。因此,如果您不需要访问派生对象的数据,您可以创建 init
函数 static
并将其传递给基 class 对象的引用。
也许这接近您的目标。
#include <iostream>
struct Base
{
Base(void (*fp)(Base&) = Base::init) { fp(*this); }
static void init(Base&) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
struct Derived : Base
{
Derived() : Base(Derived::init) { }
static void init(Base&) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
int
main()
{
Base b {};
std::cout << std::endl;
Derived d {};
}
输出:
static void Base::init(Base&)
static void Derived::init(Base&)
这里,基础 class 构造函数接受一个指向初始化函数的函数指针,该函数引用 Base
对象。该函数默认为 Base::init
但派生的 classes 可以替换它。但是请注意,在此设计中,Base
class 构造函数可能无法安全地假设 Base::init
的任何副作用实际发生了。不过,它作为一种扩展机制很好(如果 Base::init
什么都不做或者是一次性的)。
但我怀疑你是否需要使用这种机器。如果你想做的——这应该是正常情况——首先初始化基对象,然后初始化派生对象,如果你简单地从各自的构造函数中调用函数,C++ 已经默认做正确的事情。
struct Base
{
Base() { this->init(); }
void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
struct Derived : Base
{
Derived() { this->init(); }
void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
// main() as above ...
输出:
void Base::init()
void Base::init()
void Derived::init()
如果我们只想调用最派生的 class' init
函数,我们可以简单地告诉基础 class 不要 运行 它自己的。
struct Base
{
Base(const bool initialize = true) { if (initialize) this->init(); }
void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
struct Derived : Base
{
Derived() : Base(false) { this->init(); }
void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
// main() as above ...
输出:
void Base::init()
void Derived::init()
假设我有
struct C {
C() { init(); };
void init() { cout << "C" << endl; };
};
struct D : public C {
D() : C() { };
void init() { cout << "D" << endl; }
};
D();
为什么我打印 "C"?如何改变这种行为(并得到 "D")。
如果我两个都想要怎么办?
您只能从 C 构造函数中删除 init()
才能不打印 "C"。
要同时打印 "D",请在 D()
构造函数中添加 init()
。
如果在某些情况下你想要打印 "C" 或 "D" 而在某些情况下不要这样做
struct C {
C() { };
void init() { cout << "C" << endl; };
};
struct D : public C {
D() : C()
{
if(some condition)
C::init();
if(some condition)
init();
};
void init() { cout << "D" << endl; }
};
D();
why I get "C" printed?
C::init()
未声明为 virtual
,因此 D
无法覆盖它。但是,即使 C::init()
被声明为 virtual
,当 init
() 在 C
的构造函数。
C++ 在派生 classes 之前构造基 classes(并在基 classes 之前析构派生类)。所以 C
的构造函数在构造 D
之前运行(并且 C
的析构函数在 D
被破坏之后运行)。 constructed/destructed 对象的 VMT 根本不指向 D
的方法 table 当 C
被 constructed/destructed 时,它指向 C
的方法 table 而不是。
How can change this behaviour (and get "D").
您不能从基classconstructor/destructor内部调用派生虚方法。在这些阶段,VMT 不包含指向派生的 class 方法 table 的指针。
这里有一个非常基本的问题:您想在一个尚不存在的对象上调用派生 class 的成员函数。
请记住,对象是通过首先构造基础子对象然后构造派生对象来构造的。因此,即使您设法应用“聪明”的技巧来实际调用派生的 class' init
函数,一旦该函数尝试访问派生对象的任何数据成员,它会造成任意伤害。另一方面,只要您不依赖于构造函数尚未建立的任何不变量,就可以只访问基础对象。因此,如果您不需要访问派生对象的数据,您可以创建 init
函数 static
并将其传递给基 class 对象的引用。
也许这接近您的目标。
#include <iostream>
struct Base
{
Base(void (*fp)(Base&) = Base::init) { fp(*this); }
static void init(Base&) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
struct Derived : Base
{
Derived() : Base(Derived::init) { }
static void init(Base&) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
int
main()
{
Base b {};
std::cout << std::endl;
Derived d {};
}
输出:
static void Base::init(Base&)
static void Derived::init(Base&)
这里,基础 class 构造函数接受一个指向初始化函数的函数指针,该函数引用 Base
对象。该函数默认为 Base::init
但派生的 classes 可以替换它。但是请注意,在此设计中,Base
class 构造函数可能无法安全地假设 Base::init
的任何副作用实际发生了。不过,它作为一种扩展机制很好(如果 Base::init
什么都不做或者是一次性的)。
但我怀疑你是否需要使用这种机器。如果你想做的——这应该是正常情况——首先初始化基对象,然后初始化派生对象,如果你简单地从各自的构造函数中调用函数,C++ 已经默认做正确的事情。
struct Base
{
Base() { this->init(); }
void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
struct Derived : Base
{
Derived() { this->init(); }
void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
// main() as above ...
输出:
void Base::init()
void Base::init()
void Derived::init()
如果我们只想调用最派生的 class' init
函数,我们可以简单地告诉基础 class 不要 运行 它自己的。
struct Base
{
Base(const bool initialize = true) { if (initialize) this->init(); }
void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
struct Derived : Base
{
Derived() : Base(false) { this->init(); }
void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
// main() as above ...
输出:
void Base::init()
void Derived::init()