只是想知道,最快的是什么?如果我有这样的基类
Class Base
{
virtual void Draw()
{
//something...
}
};
然后我会像这样有一个Base数组:
Base Array[255];
哪个可能包含Base及其衍生物。这将是例如存储各种绘图命令的一种方式。 (我知道这看起来像java一样,但它只是用于示例。只有一个函数的类没有多大意义。)
现在另外,如果我确切知道我会做什么衍生物,可以这样做
class Base
{
int ID;
};
然后像以前一样的Base数组: 基础数组[255];
然后在衍生物中创建Draw函数:
class Der1 : Base
{
void Draw()
{
}
};
class Der2 : Base
{
void Draw()
{
}
};
现在,这个解决方案当然不允许我循环遍历数组并为每个对象调用Draw。相反,它必须做这样的事情。
void CallDraw()
{
for (int i = 0; i < 255; i++)
{
switch(Array[i].ID)
{
case 1: //Der1
( (Der1) Array[i] ) . Draw();
case 2: //Der2
( (Der2) Array[i] ) . Draw();
}
是的,对于那些已经阅读过的人来说,实际的问题。如果你知道衍生品,哪个会更快?自己制作一个有组织的系统,还是使用虚拟功能?
还有其他需要考虑的事项吗? (代码清洁可能,但我更喜欢在代码中展示我的类类型,所以我不会被演员打扰。
答案 0 :(得分:4)
绝对使用虚拟功能,这正是它们的用途。自己重新实现它是容易出错的,当然不会更快。
此外,在您的Array
声明中,您需要使用指针:
Base *Array[255];
否则,在C ++中(与Java不同),数组只能 包含Base
的实例,而不包含派生类的对象。使用指针更像是Java的引用,并允许您将派生类的实例放入数组中。
答案 1 :(得分:2)
使用虚函数,因为在逻辑上它们的行为方式大致相同,但使用虚函数表查找,这可能更慢/更快(取决于实现)。 尽管如此,除非这是你的代码的关键部分(并且是99/100个案例),否则不要为这些事情烦恼。
虚拟函数的使用使您的代码更清晰,您不必为自己实现已经作为C ++的一部分存在的事物而烦恼。
答案 2 :(得分:1)
我认为你是出于好奇而不是因为你真的试图用开关来实现它。
您必须制作阵列Base*
,而不是Base
虚拟调用是数组查找和函数调用。开关通常实现为简单的恒定时间跳转。所以,它们都是O(1),但我认为虚拟调用会更快,因为C ++编译器会被优化以使速度更快,并且交换机不能保证为O(1)。
答案 3 :(得分:1)
不要声明Base类型的值数组:object slicing将会发生,而且,您将丢失虚拟调用。
虚拟调用“慢”,但更具可读性。实际上,像这样的虚拟化调用几乎没有任何优势。如果您正在尝试优化代码,我打赌有更好的地方可以看。
答案 4 :(得分:0)
寻找虚拟功能。周期。
答案 5 :(得分:0)
我坚决认为,为了清洁代码和可维护性,您坚持使用虚拟功能。没有理由避免它们。它们在这一点上相当有效(像Java这样的语言默认使用它们用于所有非静态方法)。如果你真的拥有必须非常高效的代码并且你试图用它来尽可能地完成每条最后一条指令,那么你需要同时编写一个带有虚函数的版本和一个没有的版本并对其进行分析。除此之外,不要担心。你试图在没有实际使用语言的内置方式的情况下完成语言本身所做的事情,这很少是一个好主意。它甚至可能更慢 - 只有剖析才能说明。只需使用虚拟功能,不用担心。
此外,Base Array[255];
无法正常工作,因为所有对象都会被剪切掉并且只是Base对象而不是派生类型。您需要使用指针:Base* Array[255];
。
答案 6 :(得分:0)
由于以下原因,我建议采用虚函数方式实现逻辑
(1)我不知道您正在使用多少案例(案例实施),但如果明天您的派生类扩展到50以上甚至更多,您将最终只编写您的案例,无论您想要什么通过向派生类提供ID来手动执行是编译器的工作,编译器本身插入初始化vptr的代码,而VTABLE.best策略是“不实现编译器可以自己实现的”。 因此,将来无论您从基础派生多少个类,您的代码都会通过虚拟函数的方式更好,更快,更易读。
(2)在使用虚函数时,你必须始终在基类中创建一个虚函数并访问派生类函数(具有相同的名称)通过向上转换(通过将派生类对象指针/引用分配给基类对象)。您的实现中没有提供任何基类指针或引用,它必须是Base * Arr [100 ]或Base&amp;编曲[100]
你在第二步做的是对象切片,你正在从对象切割基础部分。
RGDS, 软质皮