我正在尝试优化代码的运行时间,并且我被告知删除不必要的虚函数是可行的方法。考虑到这一点,我仍然希望使用继承来避免不必要的代码膨胀。我想如果我只是重新定义了我想要的函数并初始化了不同的变量值,只要我需要派生类特定的行为,我就可以通过向下转换到我的派生类。
所以我需要一个标识我正在处理的类的类型的变量,所以我可以使用switch语句正确地向下转换。我使用以下代码来测试这种方法:
Classes.h
#pragma once
class A {
public:
int type;
static const int GetType() { return 0; }
A() : type(0) {}
};
class B : public A {
public:
int type;
static const int GetType() { return 1; }
B() : {type = 1}
};
Main.cpp的
#include "Classes.h"
#include <iostream>
using std::cout;
using std::endl;
using std::getchar;
int main() {
A *a = new B();
cout << a->GetType() << endl;
cout << a->type;
getchar();
return 0;
}
我得到预期的输出:0 1
问题1:有没有更好的方法来存储类型,这样我就不需要为创建的对象的每个实例浪费内存(就像static关键字允许的那样)?
问题2:将switch语句放在函数中以决定它应该根据类型值或switch语句 - >生成更有效吗?然后使用派生类特定函数。
问题3:有没有更好的方法来解决这个问题,我完全忽略了不使用虚函数?例如,我应该创建一个具有许多相同变量的全新类
答案 0 :(得分:2)
问题1:有没有更好的方法来存储类型,这样我就不需要为创建的对象的每个实例浪费内存(就像static关键字允许的那样)?
已经使用RTTI启用了typeid()
,您无需以容易出错且不可靠的方式实现自己。
问题2:将switch语句放在函数中以决定它应该根据类型值或switch语句 - >生成更有效吗?然后使用派生类特定函数。
肯定没有!这是糟糕(sic!)类继承层次结构设计的重要指标。
问题3:有没有更好的方法来解决这个问题,我完全忽略了不使用虚函数?例如,我应该创建一个具有许多相同变量的全新类
在不使用virtual
函数的情况下实现多态的典型方法是CRTP(又名静态多态)。
这是一种广泛使用的技术,可以在您不需要它时避免virtual function tables的开销,并且只是想调整您的特定需求(例如,对于小目标,低内存开销至关重要)。
鉴于您的示例 1 ,这将是这样的:
template<class Derived>
class A {
protected:
int InternalGetType() { return 0; }
public:
int GetType() { static_cast<Derived*>(this)->InternalGetType(); }
};
class B : public A<B> {
friend class A<B>;
protected:
int InternalGetType() { return 1; }
};
所有绑定都将在编译时完成,并且运行时间开销为零
如果static_cast
实际上没有继承B
,那么使用A<B>
保证安全保证绑定也会导致编译错误。
请勿将该图案用作金锤!它也有它的缺点:
这不适用于插件,就像架构模型一样,你真的想要后期绑定,并在运行时加载模块。
如果您对可执行文件的代码大小和性能没有很严格的限制,那么就不值得做额外的工作了。对于大多数系统,您可以简单地忽略使用virtual
函数定义完成的调度开销。
1) GetType()
的语义不一定是最好的,但是......
答案 1 :(得分:0)
继续使用虚函数,但要确保每个函数都做了足够的工作,间接调用的开销是微不足道的。这应该不是很难做到,虚拟调用非常快 - 如果不是,它就不会成为C ++的一部分。
执行自己的指针转换可能会更慢,除非您可以使用该指针很多次。
为了使这更具体一点,这里有一些代码:
class A {
public:
int type;
int buffer[1000000];
A() : type(0) {}
virtual void VirtualIncrease(int n) { buffer[n] += 1; }
void NonVirtualIncrease(int n) { buffer[n] += 1; }
virtual void IncreaseAll() { for i=0; i<1000000; ++i) buffer[i] += 1; }
};
class B : public A {
public:
B() : {type = 1}
virtual void VirtualIncrease(int n) { buffer[n] += 2; }
void NonVirtualIncrease(int n) { buffer[n] += 2; }
virtual void IncreaseAll() { for i=0; i<1000000; ++i) buffer[i] += 2; }
};
int main() {
A *a = new B();
// easy way with virtual
for (int i = 0; i < 1000000; ++i)
a->VirtualIncrease(i);
// hard way with switch
for (int i = 0; i < 1000000; ++i) {
switch(a->type) {
case 0:
a->NonVirtualIncrease(i);
break;
case 1:
static_cast<B*>(a)->NonVirtualIncrease(i);
break;
}
}
// fast way
a->IncreaseAll();
getchar();
return 0;
}
使用类型代码切换的代码不仅难以阅读,而且可能更慢。在虚拟功能中做更多工作最终会变得最干净,最快。