C ++非常不同的派生类 - 虚拟方法?演员?

时间:2015-11-08 22:34:17

标签: c++ inheritance polymorphism virtual

我有一个类层次结构,在基类中有很多共享成员函数,在两个派生类中也有大量独特的成员函数:

    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

6 个答案:

答案 0 :(得分:1)

  • 您可以使用static_cast投射它。最不可取的选择。如果你正在铸造东西,通常意味着你的设计需要更多的思考
  • 如果您有一个需要其中一个的特定功能/类,请将输入声明为您需要的类型,以便更准确地传达函数或类的要求
  • 如果函数需要是泛型的,并且那些方法不需要任何输入,那么你可以在父类中定义一个虚方法,比如说init,它在派生类中调用正确的方法设置实例。

答案 1 :(得分:0)

我还没有评论,这是我真正想做的事情,但我也有兴趣搞清楚这一点。

几个月前我遇到了类似的问题。我可以告诉你的是你可以使用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的事情,因此,施法是一个坏主意,但至于为什么,我不能告诉你。我希望有一个更有经验的程序员解释它。

我可以告诉你的是,在这个简单的例子中,这没有问题,你已经避免了在basetype中声明无意义的虚函数的问题。

编辑:令我惊讶的是,我经常忘记使用谷歌:Why is usage of the typeid keyword bad design?

如果我理解正确的Bolas,那么typeid会激励糟糕的编码实践。但是,在您的示例中,您希望访问子类型非虚函数。据我所知,如果不在编译时检查类型,即没有办法做到这一点,即typeid。

答案 2 :(得分:0)

我的编译器项目中存在类似的问题,其中AST(抽象语法树)是从语句构造的,因此while(x != 0) { if (a == b) x = 0; }将构建一个whileAST,其中包含binaryExpr ,然后使用blockAST ifAST,依此类推。它们中的每一个都有一些共同的属性,而且很多东西只适用于你实际执行特定于该部分的事情。大多数情况下,这是通过调用通用(虚拟)成员函数来解决的。

然而,有时您需要做非常具体的事情。有两种方法可以做到这一点:

  • 使用dynamic_cast(或typeid + reinterpret_caststatic cast)。
  • 设置了几十个virtual成员函数,这些函数大部分都是无用的(没有做任何事情或返回某种“无法做到”的指示)

就我而言,我选择了第一个。它不应该是常见的情况,但有时它确实是正确的事情。

所以在这种情况下,你会做类似的事情:

AnimatedScene *animScene = dynamic_cast<AnimatedScene*>(&scene);
if (!animScene) 
{ 
   ... do something else, since it's not an AnimatedScene ...
}
animScene->SetupAnimation();

答案 3 :(得分:0)

如果您的层次结构出现此类问题,则证明层次结构过于笼统。您可能希望实现接口模式,如果类具有某些功能,它将继承定义该功能的接口。

答案 4 :(得分:0)

证明狗是动物,所有的动物都可以,但是狗不会吠叫,或只做狗吠?

第一种方法导致class animal失败整个动物园的所有经文,在每只动物中逐一实施。特别是class dog将仅覆盖bark() 在这种方法中,动物变成了一种“上帝对象”(知道一切),需要在每次引入新内容时不断更新,并要求在其之后重新创建(重新编译所有内容)整个“宇宙”。 p>

第二种方法首先需要检查动物是狗(通过dynamic cast)然后让它吠叫。 (或在询问cat)之前检查mieow

权衡可能包括理解你是否有可能经常检查吠叫的可能性(不知道你处理的是哪种动物),如何报告失败,以及如何做以防万一这种失败。

换句话说,关键驱动程序不是树皮,而是程序中的上下文

答案 5 :(得分:-1)

//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;
}