如何从基类构造函数调用派生类虚拟方法
How to call derived class virtual method from base class constructor?
class MyClass1
{
public:
MyClass1()
{
init();
}
virtual void init()
{
printf("MyClass1 init");
}
}
class MyClass2 : public MyClass1
{
public:
virtual void init()
{
printf("MyClass2 init");
}
}
int main()
{
MyClass2 *obj = new MyClass2();
return 0;
}
我希望这个结果
"MyClass2 init"
但它实际上显示了消息
"MyClass1 init"
如何从基类构造函数调用派生类虚拟方法?
=== 更新1 ===
class MyClass1
{
public:
MyClass1()
{
init();
}
virtual void init()
{
printf("MyClass1 init");
}
}
class MyClass2 : public MyClass1
{
public:
MyClass2()
: MyClass1()
{
}
virtual void init()
{
printf("MyClass2 init");
}
}
我希望 MyClass2 覆盖 MyClass1 初始化方法但它仍然显示"MyClass1 init"
C++如何像java/C#覆盖方法一样工作?
=== 更新2 ===
class MyClass1
{
public:
MyClass1()
{
init(); <--- can't work like test method ???
}
void test()
{
init(); <--- work fine
}
virtual void init()
{
printf("MyClass1 init");
}
}
class MyClass2 : public MyClass1
{
public:
MyClass2()
: MyClass1()
{
}
virtual void init()
{
printf("MyClass2 init");
}
}
我知道obj->init()会调用MyClass2::init。
但我希望C++可以在构造函数方法中运行。
虽然obj->init()可以解决。
但我希望代码可以少写一点。
有些人忘记调用 init()。
Java/C#可以少写多做更多的事情。但C++不能....这是非常令人沮丧的。
显然在构造MyClass2
的过程中,MyClass1
的构造函数将首先调用,即派生类对象的基类部分是在派生类部件之前构造的。即使你明确地尝试创建MyClass2
对象,但在基类构造期间,虚函数永远不会进入派生类。
之前执行,因此在基类构造函数运行时尚未初始化派生类数据成员。如果在基类构造期间调用的虚函数下降到派生类,则派生类函数几乎肯定会引用本地数据成员,但这些数据成员尚未初始化,这将导致未定义的行为。
参考你的班级,你为什么首先有virtual init
?构造函数应分别完成工作。
如何从基类构造函数调用派生类虚拟方法?
这是个坏主意,永远不应该这样做!
class MyClass1
{
public:
virtual ~MyClass1() { }
virtual void doSomething()
{
printf("MyClass1 init");
}
};
class MyClass2 : public MyClass1
{
public:
virtual void doSomething()
{
printf("MyClass2 init");
}
};
int main()
{
MyClass1*obj = new MyClass2();
obj->doSomething();
delete obj;
return 0;
}
一般来说,你不能。这是因为 MyClass2
构造函数尚未运行,因此如果要调用MyClass2::init()
,则任何成员变量都将未初始化。在此构造函数启动之前,出于虚拟目的,对象被视为MyClass1
实例。
例如:
class MyClass2 : public MyClass1
{
private:
std::string xxx;
public:
virtual void init()
{
xxx = "ops!"; //undefined if called from base class constructor
}
};
对xxx
赋值将呈现未定义的行为,因为其构造函数尚未运行。
鉴于你缺乏结尾;
我猜你在 Java/C# 方面的背景。这些语言以不同的方式实现了这个问题,但这就是C++的工作方式。
我不确定这是否合法或将来可能会破裂。但是,使用 lambda 函数,您可以将代码发送到基类以在构造函数期间执行。
class Base {
public:
Base(std::function<void()> fnCall=nullptr) {
if (fnCall) fnCall();
}
Base(const Base &base, std::function<void()> fnCall = nullptr) {
DoSomethingAtBaseHere();
if (fnCall) fnCall();
}
};
class Derived: public Base {
public:
Derived(std::function<void()> fnCall=nullptr) : Base([&]() {
DoSomethingAtDerivedHere();
if (fnCall) fnCall();
}) {}
Derived(const Derived &oth, std::function<void()> fnCall=nullptr) :
Base(oth, [&]() {
DoSomethingInCopyConstructorAtDerivedHere();
if (fnCall) fnCall();
}) {}
};
我认为:
没有理由C++不能做java/C#虚拟构造器机制。
我想打破C++规则。
我也希望C++能少写多做事。
如果C++不提高,就不会进步,让更多的人无法轻易进入。
class MyClass1
{
public:
MyClass1()
{
init();
}
typedef void (MyClass1::virtual_init)();
MyClass1(virtual_init vinit)
{
(this->*vinit)();
}
//virtual void init()
void init()
{
printf("MyClass1 init");
}
}
class MyClass2 : public MyClass1
{
public:
MyClass2()
: MyClass1(&MyClass2::init)
{
}
//virtual void init()
void init()
{
printf("MyClass2 init");
}
}
我遇到了同样的问题,我使用了CRTP(奇怪的重复模板模式)来解决这个问题:
#include <stdio.h>
#define derived static_cast<T*>(this)
template<class T> // CRTP
class MyClass1
{
public:
MyClass1()
{
derived->init();
}
void init()
{
printf("MyClass1 initn");
}
};
class MyClass2 : public MyClass1<MyClass2>
{
public:
void init()
{
printf("MyClass2 initn");
}
};
int main()
{
MyClass2 *obj2 = new MyClass2();
return 0;
}
此程序按预期打印MyClass2 init
,而不是MyClass init
。如果派生类中的函数不存在,则调用父类函数。
CRTP 的优点:
- 静态编译的函数调用
- 比虚拟功能更快
- 可以控制调用哪个方法
CRTP 的缺点:
- 可读性稍差 从
- 父类派生类调用的所有函数都必须
public
。相应的父类函数也必须是公共的。 - 需要将类名作为模板参数传递
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 大小虚拟继承中的派生类
- 是否可以使用基类非虚拟方法中的派生类虚拟方法?
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 虚拟基类函数中派生类的大小
- 从纯虚拟类 (A) 派生的指针无法访问来自纯类 (B) 的重载方法
- 使(虚拟)函数在大多数派生类中无法访问中间基类中可访问,定义良好?
- 派生类中纯虚拟基方法的专业化
- 基类可以声明虚拟方法但不定义它吗?仍然在派生类中定义
- 无法在派生对象上运行虚拟函数
- googletest:测试基类具有纯虚拟方法的派生类时的核心转储
- 使用回调函数从构造函数调用虚拟/派生方法的替代方法?
- 有没有办法在没有虚拟的情况下使用基类指针调用派生类函数
- 从基调用派生类的隐藏(非虚拟)方法
- 派生类可以有多个指向虚拟表的指针
- 如何从派生类函数中调用虚拟函数
- C++虚拟函数:基类函数是调用的,而不是派生的
- 空类的大小和派生虚拟类
- C++ STL:使用派生虚拟类作为 std::sort() 的"Strict Weak Ordering"