问题描述
|
众所周知,STL类不在任何地方使用虚拟方法(并且STL也不在任何地方使用继承,并且这两个事实是相互关联的),并且STL并不是唯一的。
假设地球上还存在其他性能怪胎[存在],这些性能怪胎会为每个类要求自己“我是否需要为此类X使用虚方法?”和“此类X是否可以不任何虚拟机,就像STL类一样,都能获得更好的性能?\“
与“ virtuals”基类相比,缺少任何虚拟方法(包括dtor)都会使多态和子类化更加困难。显然,“非虚拟”类不太适合作为基类。
问题:是否有技术(用于c ++)允许程序员一次性创建两个相同类X的版本,一个“非虚拟”版本Xnv(用于性能)和一个“虚拟”版本Xv,用于子类化?如果不需要,请说明原因。
Post-note
人们回答
\“如果需要子类化,请使用虚拟。如果不需要,请不要使用虚拟\”。
这个建议有问题。几个问题。
1)需求经历了随着时间变化的变化。那时不需要从类X继承子类,但是现在需要,反之亦然。
2)写基类的人与写派生类的人不同。从问题中可以清楚地看出这一点。人们有不同的思维方式,不同的判断和不同的需求。再说一遍
3)因此,不同的程序员回答“是否从类X继承是否有意义?”之类的问题,将给出不同的答案。这是主观的,没有明确的答案。
4)与所问的问题相矛盾。
因此,我们希望满足频谱的两端(这在工程中经常发生),这是问题背后的动力。
动机太复杂,无法在问题中简明扼要地表达出来。我认为人们可以(1)假设问题已经被精确地提出来就存在动机,或者可以(2)因为人们已经处于c ++设计的权衡平衡情况下而计算动机。
没有人知道这个动机-令我惊讶的是-甚至现在。这对我来说是一个教训。
我接受了提到CRTP的答案,因为它是一种有趣的模式。
解决方法
问题:是否有技术(用于c ++)允许程序员一次性创建两个相同类X的版本,一个“非虚拟”版本Xnv(用于性能)和一个“虚拟”版本Xv,用于子类化?
当您可以吃蛋糕时也为什么要这样做呢?
使用CRTP,您具有编译时多态性,并且子类具有覆盖行为的能力,而虚拟函数没有任何开销。
或者,您可以使用\“ traits \”类来注入行为。
, 我想你可以...
struct Base
{
virtual ~Base();
virtual void foo();
};
struct Dummy {};
template <bool is_virtual>
struct SelectBase
{
typedef Base type;
};
template <>
struct SelectBase<false>
{
typedef Dummy type;
};
template <bool is_virtual>
struct MyClass : SelectBase<is_virtual>::type
{
~MyClass();
void foo();
};
int main()
{
Base* xv = new MyClass<true>(); // virtual version
MyClass<false>* xnv = new MyClass<false>(); // non-virtual version
xv->foo(); // virtual call
xnv->foo(); // non-virtual call
}
我真的没有想到这样做的充分理由。
, 与其先担心性能,不如说一个更好的问题是“从此类继承是否有点道理?”如果答案是否定的,那么为什么要虚拟化任何东西?毕竟,从本质上讲,最终课程具有一些存储和性能优势。 (尽管C ++不支持Java类final
类的概念,但是没有虚拟方法的类几乎可以被称为'final \'。)
但是,我通常走相反的路线:我倾向于将析构函数虚拟化,因为其他人可能会看到从类继承的用途。
, 我认为您对决定是否使方法虚拟化的原因感到困惑。性能不是使您的方法具有虚拟性或非虚拟性的原因之一,事实上,性能的影响往往很小。[1]
是否提供虚拟方法的决定应基于您的要求,尤其是:您是否需要运行时多态性?如果这样做,则这些方法应该是虚拟的,如果不这样做,则它们应该不是。
[1]在具有单继承的设计中,虚拟分派仅采用一个额外的间接,而在多分派中,代价会稍高一些,可能会产生额外的指针偏移和间接。这样的代价将比该方法执行的任何操作都要小得多。
,另一件事是您以后可以简单地编写它。
template<typename T> class something {
virtual const T& operator*() = 0;
virtual something<T>& operator++() = 0; // This should be only prefix
// etc
};
template<typename Derived,typename T> class something_better : public something<T> {
typename Derived::const_iterator i;
public:
something_better(const Derived& d) {
i = d.begin();
}
const T& operator*() {
return *i;
}
something<T>& operator++() {
++i;
return *this;
}
something<T>& operator--() {
--i;
return *this;
}
};
等等,运行时多态迭代。那很简单。当然,只要迭代器和容器继续有效即可。
如果要进行非const迭代等操作,则需要等到C ++ 0x正确处理rvalues。或问“四”人,他们似乎已经以某种方式解决了问题。
, 我对此假设表示怀疑:
就像STL类一样,为了获得更好的性能?
我不同意缺乏虚拟性是出于性能方面的考虑。
与“ virtuals”基类相比,缺少任何虚拟方法(包括dtor)都会使多态和子类化更加困难。
非常真实这就是我在容器类中没有虚函数的原因。它是故意设计成不能被子类化的。使用容器类时,最好使用成员资格而不是继承。
问题:是否有技术(用于c ++)允许程序员一次性创建两个相同类X的版本,一个“非虚拟”版本Xnv(用于性能)和一个“虚拟”版本Xv,用于子类化?如果不需要,请说明原因。
你为什么想这么做。
如果为性能而设计,那么您的界面通常会从基本的OO界面更改。在暴露内部信息时,您往往会变得松懈一些(因为通常是为了更紧密地耦合类而交易速度)。
为了获得更高的性能,该算法通常会更多地了解内部原理,以便可以使用此信息对其工作原理进行假设。因此,您可以以将存储耦合到使用该存储的算法为代价来提高性能。