C++截然不同的派生类 - 虚拟方法?演员表
C++ Vastly Different Derived Classes - Virtual methods? A cast?
我有一个类层次结构,基类中有许多共享成员函数,并且在两个派生类中还有大量唯一的成员函数:
class Scene {
public:
void Display();
void CreateDefaultShapes();
void AddShape(Shape myShape);
// lots more shared member functions
}
Class AnimatedScene : public Scene {
public:
void SetupAnimation();
void ChangeAnimationSpeed(float x, float y);
// lots of member functions unique to animation
}
Class ControllableScene : public Scene {
public:
void ConfigureControls();
void MoveCamera(float x, float y, float z);
void Rotate(float x, float y, float z);
// lots of member functions unique to a controllable scene
}
毫不奇怪,这不起作用:
Scene myScene;
myScene.SetupAnimation();
这个问题的正确解决方案是什么?我是否应该将所有派生成员函数都设置为虚拟并将它们添加到基函数中?调用SetupAnimation()
时我应该使用演员表吗?有没有更聪明的设计来解决这个问题?
注意:我从其他地方收到myScene
,不能在实例化时简单地将其声明为AnimatedScene
。
编辑:我添加了更多成员函数来说明这一点。少数初始化函数绝对适合简单地使用 virtual
.
- 您可以强制转换它,最好使用
static_cast
.最不可取的选择。如果你正在铸造东西,这通常意味着你的设计需要更多的思考。 - 如果你有一个特定的函数/类需要其中一个,请将输入声明为你需要的类型,这样可以更准确地传达函数或类的要求
- 如果函数需要泛型,并且这些方法不需要任何输入,则可以在父类中定义一个虚拟方法,例如
init
,该方法在派生类中调用正确的方法来设置实例。
我的编译器项目中也有类似的问题,其中 AST(抽象语法树(是从语句构造的,因此while(x != 0) { if (a == b) x = 0; }
会构造一个内部带有binaryExpr
的whileAST
,然后构造一个带有ifAST
的blockAST
,依此类推。它们中的每一个都有一些共同的属性,并且很多事情只有在您实际执行特定于该部分的操作时才适用。大多数情况下,这是通过调用泛型(虚拟(成员函数来解决的。
但是,有时您需要做非常具体的事情。有两种方法可以做到这一点:
- 使用
dynamic_cast
(或typeid
+reinterpret_cast
或static cast
(。 - 设置数十个
virtual
成员函数,这些函数大多是完全无用的(不执行任何操作或返回某种"无法执行此操作"指示(
就我而言,我选择第一个。这不应该是常见的情况,但有时它确实是正确的做法。
因此,在这种情况下,您可以执行以下操作:
AnimatedScene *animScene = dynamic_cast<AnimatedScene*>(&scene);
if (!animScene)
{
... do something else, since it's not an AnimatedScene ...
}
animScene->SetupAnimation();
我还不能发表评论,这是我真正想做的,但我也有兴趣弄清楚这一点。
几个月前,我遇到了类似的问题。我可以告诉你的是,你可以使用 typeid 运算符来确定对象的类型,如下所示:
int main()
{
scene* ptr = new AnimatedScene();
if (typeid(*ptr) == typeid(AnimatedScene))
{
cout<<"ptr is indeed a animatedScene"<<endl;
AnimatedScene* ptr2 = (AnimatedScene*)(ptr);
ptr2->SetupAnimation();
}
else
{
cout<<"not a animatedscene!!!"<<endl;
}
}
这样,您就可以使用 ptr2 访问 animatedScene 的唯一成员。
请注意指针的使用,你不能直接使用对象,因为在玩多态性时称为"对象切片"的东西:https://en.wikipedia.org/wiki/Object_slicing
像你一样,我也听说过一些关于使用typeid的事情,因此,铸造是一个坏主意,但至于为什么,我不能告诉你。我希望有一个更有经验的程序员来解释它。
我可以告诉你的是,在这个简单的示例中,这没有问题,你已经避免了在基类型中声明无意义的虚函数的问题。
编辑:令人惊讶的是,我经常忘记使用谷歌:为什么使用typeid关键字的设计很糟糕?
如果我正确理解了 Bolas 先生,typeid 会激励不良的编码实践。但是,在您的示例中,您希望访问子类型非虚函数。据我所知,如果不在编译时检查类型,即typeid,就没有办法做到这一点。
如果您的层次结构出现此类问题,则证明层次结构过于笼统。你可能想要实现接口模式,如果类具有某些功能,它将继承定义该功能的接口。
证明狗是动物,除了狗之外,所有的动物都不会吠叫,还是只有狗会吠叫?
第一种方法导致class animal
使整个动物园的所有经文失败,在每只动物中逐一实施。特别是class dog
将覆盖仅bark()
.
在这种方法中,动物成为一种"神对象"(知道一切(,每次引入新事物时都需要不断更新,并且要求在它之后重新创建整个"宇宙"(重新编译一切(。
第二种方法要求首先检查动物是狗(通过dynamic cast
(,然后要求它吠叫。(或在询问mieow
之前检查cat
(
权衡可能包括了解您必须检查树皮脱离上下文的可能性的频率(不知道您正在处理哪种动物(,如何报告失败,以及在发生此类失败时该怎么做。
换句话说,关键驱动因素不是树皮,而是程序内围绕它的上下文。
//Are you trying to do something like this?
class Scene{
public:
virtual void Display()=0; //Pure virtual func
};
class AnimatedScene : public Scene{
public:
void Display(){
std::cout<<"function Display() inside class AnimatedScene" <<std::endl;
}
};
class ControllableScene : public Scene{
public:
void Display(){
std::cout<<"function Display() inside class ControllableScene" <<std::endl;
}
};
int main(){
AnimatedScene as;
ControllableScene cs;
Scene *ex1 = &as;
Scene *ex2 = &cs;
ex1->Display();
ex2->Display();
return 0;
}
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- 是否可以使用基类非虚拟方法中的派生类虚拟方法?
- 如何编写 operator= 用于使用虚拟方法与非平凡成员的匿名联合
- 让编译器告诉什么确切的纯虚拟方法使结构抽象?
- 使用模板而不是虚拟方法的管道模式
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 从纯虚拟类 (A) 派生的指针无法访问来自纯类 (B) 的重载方法
- 为什么调用没有正文的纯虚拟方法不会导致链接器错误?
- 出于什么目的,非虚拟方法将与C++一起使用?
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 如何重写继承的嵌套类中存在的虚拟方法
- 私有虚拟方法有什么用?
- 派生类中纯虚拟基方法的专业化
- 基类可以声明虚拟方法但不定义它吗?仍然在派生类中定义
- googletest:测试基类具有纯虚拟方法的派生类时的核心转储
- 确保模拟的 GTest 方法覆盖虚拟方法
- 使用回调函数从构造函数调用虚拟/派生方法的替代方法?
- 如何调用孩子的方法:虚拟关键字不起作用