众所周知,STL类在任何地方都不使用虚方法(并且STL也不在任何地方使用继承,并且这两个事实是相互关联的),并且STL并不是唯一的。
让我们假设地球上存在其他表现怪胎[他们存在],这样的表演狂热者会问自己每个班级“我是否需要这个X级的虚拟方法?”并且“这个类X可以没有任何虚拟,就像STL类一样,以获得更好的性能吗?”
任何虚拟方法(包含d'tor)的缺失使得多态性和子类化比“虚拟”基类更难。显然,“非虚拟”类不非常适合作为基类。
问题:是否有技术(对于c ++)允许程序员一次性创建 同一类X的两个版本,“非虚拟”版本Xnv(用于性能)和“虚拟” “版本Xv,用于子类化?如果不需要,请解释原因。
Post-note
人们回答 “如果您需要子类,请使用虚拟。如果不需要,请不要使用虚拟”。
此建议存在问题。几个问题。
1)需要改变随时间的变化。那时不需要从X类继承,但现在需要,反之亦然
2)编写基类的人与编写派生类的人不同。这个问题很清楚。人们有不同的思维方式,不同的判断,不同的需求。再次清楚。
3)因此,不同的程序员回答诸如“从X类继承是否有意义?”这样的问题,将给出不同的答案。这是主观的,没有切入式的答案
4)它与提出的问题相矛盾。
因此,我们希望满足频谱的两个方面 - 这通常发生在工程中 - 这就是问题背后的动机。
动机过于复杂,无法在问题中简明扼要地表达出来。我认为人们可以(1)假设动机存在,因为问题是精确制定的,或者可以(2)计算动机因为他们已经处于c ++设计的类似权衡和平衡状态。
没有人想出这个动机 - 令我惊讶的是 - 甚至可能是现在。这对我来说应该是一个教训。
我接受了提到CRTP的答案,因为这是一种搞笑的模式。
答案 0 :(得分:1)
我想你可以......
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
}
我无法想到这样做的好理由。
答案 1 :(得分:1)
问题:是否有技术(对于c ++)允许程序员一次创建两个版本的同一个类X,一个“非虚拟”版本Xnv(用于性能)和“虚拟”版本Xv,用于子类化?
为什么这样,你什么时候可以吃蛋糕呢?
使用CRTP,您具有编译时多态性,以及子类覆盖行为的能力,而不会对虚函数产生任何开销。
或者,您可以使用“traits”类来注入行为。
答案 2 :(得分:1)
不是先担心性能问题,而是一个更好的问题是“从这个阶级继承是否会让人感到尴尬?”如果答案是否定的,为什么要虚拟化?对于本质上是最终的类而言,总有一些存储和性能优势。 (虽然C ++不支持final
类的Java概念,但没有虚拟方法的类非常接近“最终”。)
但是,我通常采用相反的方式:我倾向于将析构函数设置为虚拟,因为其他人可能会看到继承该类的用法。
答案 3 :(得分:0)
我认为你对决定使方法变为虚拟的原因感到困惑。性能不使您的方法变为虚拟的一个原因,事实上,性能的影响通常是最小的 [1] 。
是否提供虚拟方法的决定应该基于您的要求,特别是:您是否需要运行时多态性?如果你这样做,那么这些方法应该是虚拟的,如果你不这样做,那么它们就不应该是虚拟的。
[1] 在具有单一继承的设计中,虚拟分派仅占用一个额外的间接,而在多个分派中,成本稍高,可能有额外的指针偏移和间接。其成本将远低于该方法的任何操作。
答案 4 :(得分:0)
另一件事是你可以稍后再写。
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;
}
};
Et voila-运行时多态迭代。那很简单。当然,只要迭代器和容器继续有效。
如果你想要非常量迭代和其他东西,那么你需要等到C ++ 0x才能正确处理rvalues。或者询问BOOST_FOREACH
人,他们似乎已经以某种方式解决了问题。
答案 5 :(得分:0)
我对这个假设有疑问:
就像STL类一样,为了更好的性能?
我不同意缺乏虚拟是出于性能原因。
缺少任何虚拟方法(包含d'tor)使得多态和子类化比“虚拟”基类更难。
非常正确。这就是我在容器类中没有虚函数的原因。故意设计不属于分类。使用容器类时最好使用成员资格而不是继承。
问题:是否有技术(对于c ++)允许程序员一次创建两个版本的同一个类X,一个“非虚拟”版本Xnv(用于性能)和“虚拟”版本Xv,用于子类化?如果不需要,请解释原因。
你为什么要那样做呢 如果您正在设计性能,那么您的界面通常会从基本的OO界面进行更改。你倾向于在暴露你的内部方面变得更加宽松(因为交易通常是交易速度,以便你的班级更紧密地结合)。
为了获得更高的性能,算法通常会更多地了解内部结构,以便它可以使用此信息来假设它是如何工作的。因此,您可以以将存储耦合到使用存储的算法为代价来提高性能。