C++ 非常不同的派生 类 - 虚拟方法?演员?
C++ Vastly Different Derived Classes - Virtual methods? A cast?
我有一个 class 层次结构,在基 class 中有很多共享成员函数,在两个派生 classes 中也有大量独特的成员函数:
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
.
//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;
}
- 你可以施法,最好使用
static_cast
。最不受欢迎的选择。如果你在铸造东西,通常意味着你的设计需要更多的思考
- 如果您有一个特定的 function/class 需要一个或另一个,请将输入声明为您需要的类型,这样可以更准确地传达函数的要求或 class
- 如果函数需要通用,并且这些方法不需要任何输入,那么您可以在父 class 中定义一个虚拟方法,比如
init
,在派生中classes 调用正确的方法来设置实例。
我还不能评论,这是我真正想做的,但我也有兴趣解决这个问题。
几个月前我遇到了类似的问题。我可以告诉你的是,你可以使用 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 的唯一成员。
注意指针的使用,你不能直接使用对象,因为在玩多态时有一个叫做 "object slicing" 的东西:https://en.wikipedia.org/wiki/Object_slicing
和你一样,我听说过一些关于使用 typeid 的事情,因此,转换是一个坏主意,但至于为什么,我不能告诉你。希望有经验的程序员解释一下。
我可以告诉你的是,在这个简单的例子中,这没有问题,你已经避免了在基类型中声明无意义的虚函数的问题。
编辑:令人惊奇的是我经常忘记使用 google:Why is usage of the typeid keyword bad design?
如果我对 Bolas 先生的理解是正确的,那么 typeid 会助长不良的编码习惯。但是,在您的示例中,您想要访问子类型非虚函数。据我所知,如果不在编译时检查类型,即 typeid,就无法做到这一点。
我在我的编译器项目中遇到了类似的问题,其中 AST(抽象语法树)是根据语句构建的,因此 while(x != 0) { if (a == b) x = 0; }
将构建 whileAST
和 binaryExpr
在其中,然后是 blockAST
和 ifAST
,依此类推。它们中的每一个都有一些共同的属性,并且很多东西只有在您实际执行特定于该部分的操作时才适用。大多数时候,这是通过调用通用(虚拟)成员函数来解决的。
但是,有时您需要做非常具体的事情。有两种方法可以做到这一点:
- 使用
dynamic_cast
(或 typeid
+ reinterpret_cast
或 static cast
)。
- 设置几十个
virtual
成员函数,其中大部分是完全无用的(不做任何事情或 return 某种 "can't do that" 指示)
就我而言,我选择第一个。这不应该是常见的情况,但有时确实是正确的做法。
所以在这种情况下,你会做类似的事情:
AnimatedScene *animScene = dynamic_cast<AnimatedScene*>(&scene);
if (!animScene)
{
... do something else, since it's not an AnimatedScene ...
}
animScene->SetupAnimation();
如果您的层次结构出现此类问题,则证明层次结构过于笼统。您可能想要实现接口模式,如果 class 具有某些功能,它将继承定义该功能的接口。
证明狗是动物,是除了狗以外的动物都不会叫,还是只有狗会叫?
第一种方法导致 class animal
失败整个动物园的所有经文,在每只动物中逐一实施。特别是 class dog
将仅覆盖 bark()
.
在这种方法中,动物变成了一种 "god object"(什么都知道),每次引入新事物时都需要不断更新,并且需要在它之后重新创建整个 "universe"(重新编译所有内容) .
第二种方法需要先检查动物是狗(通过dynamic cast
)然后让它吠叫。 (或在询问 mieow
之前检查 cat
)
权衡可能包括了解您必须多久检查一次树皮(不知道您在处理哪种动物)、如何报告失败以及该怎么做以防万一。
换句话说,关键驱动因素不是树皮,而是程序中围绕它的上下文。
我有一个 class 层次结构,在基 class 中有很多共享成员函数,在两个派生 classes 中也有大量独特的成员函数:
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
.
//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;
}
- 你可以施法,最好使用
static_cast
。最不受欢迎的选择。如果你在铸造东西,通常意味着你的设计需要更多的思考 - 如果您有一个特定的 function/class 需要一个或另一个,请将输入声明为您需要的类型,这样可以更准确地传达函数的要求或 class
- 如果函数需要通用,并且这些方法不需要任何输入,那么您可以在父 class 中定义一个虚拟方法,比如
init
,在派生中classes 调用正确的方法来设置实例。
我还不能评论,这是我真正想做的,但我也有兴趣解决这个问题。
几个月前我遇到了类似的问题。我可以告诉你的是,你可以使用 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 的唯一成员。
注意指针的使用,你不能直接使用对象,因为在玩多态时有一个叫做 "object slicing" 的东西:https://en.wikipedia.org/wiki/Object_slicing
和你一样,我听说过一些关于使用 typeid 的事情,因此,转换是一个坏主意,但至于为什么,我不能告诉你。希望有经验的程序员解释一下。
我可以告诉你的是,在这个简单的例子中,这没有问题,你已经避免了在基类型中声明无意义的虚函数的问题。
编辑:令人惊奇的是我经常忘记使用 google:Why is usage of the typeid keyword bad design?
如果我对 Bolas 先生的理解是正确的,那么 typeid 会助长不良的编码习惯。但是,在您的示例中,您想要访问子类型非虚函数。据我所知,如果不在编译时检查类型,即 typeid,就无法做到这一点。
我在我的编译器项目中遇到了类似的问题,其中 AST(抽象语法树)是根据语句构建的,因此 while(x != 0) { if (a == b) x = 0; }
将构建 whileAST
和 binaryExpr
在其中,然后是 blockAST
和 ifAST
,依此类推。它们中的每一个都有一些共同的属性,并且很多东西只有在您实际执行特定于该部分的操作时才适用。大多数时候,这是通过调用通用(虚拟)成员函数来解决的。
但是,有时您需要做非常具体的事情。有两种方法可以做到这一点:
- 使用
dynamic_cast
(或typeid
+reinterpret_cast
或static cast
)。 - 设置几十个
virtual
成员函数,其中大部分是完全无用的(不做任何事情或 return 某种 "can't do that" 指示)
就我而言,我选择第一个。这不应该是常见的情况,但有时确实是正确的做法。
所以在这种情况下,你会做类似的事情:
AnimatedScene *animScene = dynamic_cast<AnimatedScene*>(&scene);
if (!animScene)
{
... do something else, since it's not an AnimatedScene ...
}
animScene->SetupAnimation();
如果您的层次结构出现此类问题,则证明层次结构过于笼统。您可能想要实现接口模式,如果 class 具有某些功能,它将继承定义该功能的接口。
证明狗是动物,是除了狗以外的动物都不会叫,还是只有狗会叫?
第一种方法导致 class animal
失败整个动物园的所有经文,在每只动物中逐一实施。特别是 class dog
将仅覆盖 bark()
.
在这种方法中,动物变成了一种 "god object"(什么都知道),每次引入新事物时都需要不断更新,并且需要在它之后重新创建整个 "universe"(重新编译所有内容) .
第二种方法需要先检查动物是狗(通过dynamic cast
)然后让它吠叫。 (或在询问 mieow
之前检查 cat
)
权衡可能包括了解您必须多久检查一次树皮(不知道您在处理哪种动物)、如何报告失败以及该怎么做以防万一。
换句话说,关键驱动因素不是树皮,而是程序中围绕它的上下文。