不,没有这样的开关。
LLVM/Clang 代码库避免了由数以万计分配的类中的虚拟表:这在封闭的层次结构中运行良好,因为单个enum
可以枚举所有可能的类,然后每个类都链接到enum
. 关闭显然是enum
因为。
然后,在调用该方法之前,虚拟性由 a switch
on theenum
和适当的强制转换实现。再次关闭。switch
必须针对每个新类进行修改。
第一种选择:外部 vpointer。
如果您发现自己经常支付 vpointer 税,那么大多数对象都是已知类型的。然后,您可以将其外部化。
class Interface {
public:
virtual ~Interface() {}
virtual Interface* clone() const = 0; // might be worth it
virtual void updateCount(int) = 0;
protected:
Interface(Interface const&) {}
Interface& operator=(Interface const&) { return *this; }
};
template <typename T>
class InterfaceBridge: public Interface {
public:
InterfaceBridge(T& t): t(t) {}
virtual InterfaceBridge* clone() const { return new InterfaceBridge(*this); }
virtual void updateCount(int i) { t.updateCount(i); }
private:
T& t; // value or reference ? Choose...
};
template <typename T>
InterfaceBridge<T> interface(T& t) { return InterfaceBridge<T>(t); }
然后,想象一个简单的类:
class Counter {
public:
int getCount() const { return c; }
void updateCount(int i) { c = i; }
private:
int c;
};
您可以将对象存储在数组中:
static Counter array[5];
assert(sizeof(array) == sizeof(int)*5); // no v-pointer
并且仍然将它们与多态函数一起使用:
void five(Interface& i) { i.updateCount(5); }
InterfaceBridge<Counter> ib(array[3]); // create *one* v-pointer
five(ib);
assert(array[3].getCount() == 5);
价值与参考实际上是一种设计张力。一般来说,如果您需要,clone
您需要按值存储,并且在您按基类存储时需要克隆(boost::ptr_vector
例如)。实际上可以同时提供两个接口(和桥接器):
Interface <--- ClonableInterface
| |
InterfaceB ClonableInterfaceB
这只是额外的打字。
另一种解决方案,涉及更多。
切换可以通过跳转表来实现。这样的表可以在运行时完美地创建,std::vector
例如:
class Base {
public:
~Base() { VTables()[vpointer].dispose(*this); }
void updateCount(int i) {
VTables()[vpointer].updateCount(*this, i);
}
protected:
struct VTable {
typedef void (*Dispose)(Base&);
typedef void (*UpdateCount)(Base&, int);
Dispose dispose;
UpdateCount updateCount;
};
static void NoDispose(Base&) {}
static unsigned RegisterTable(VTable t) {
std::vector<VTable>& v = VTables();
v.push_back(t);
return v.size() - 1;
}
explicit Base(unsigned id): vpointer(id) {
assert(id < VTables.size());
}
private:
// Implement in .cpp or pay the cost of weak symbols.
static std::vector<VTable> VTables() { static std::vector<VTable> VT; return VT; }
unsigned vpointer;
};
然后,一个Derived
类:
class Derived: public Base {
public:
Derived(): Base(GetID()) {}
private:
static void UpdateCount(Base& b, int i) {
static_cast<Derived&>(b).count = i;
}
static unsigned GetID() {
static unsigned ID = RegisterTable(VTable({&NoDispose, &UpdateCount}));
return ID;
}
unsigned count;
};
好吧,现在你会意识到编译器为你做这件事是多么的棒,即使是以一些开销为代价。
哦,由于对齐,一旦一个Derived
类引入一个指针,就有可能在Base
和下一个属性之间使用 4 个字节的填充。您可以通过仔细选择前几个属性来使用它们Derived
以避免填充...