【发布时间】:2016-03-07 13:50:27
【问题描述】:
我正在阅读 Scott Meyers 的 Effective C++,我正在阅读关于继承的部分。他说
纯虚函数只指定接口的继承。
简单(不纯)虚函数指定接口的继承 加上默认实现的继承。
现在,考虑以下两个类:
struct A {
virtual void foo() = 0;
};
void A::foo(){ std::cout << "Default foo" << std::endl; }
struct B : A{
virtual void foo(){ A::foo(); std::cout << "Derived foo" << std::endl; }
};
我们仍然可以像我在示例中所做的那样为纯虚函数提供默认实现,并通过qualified-function-call-expression 调用它。对于不纯的虚函数,我们几乎可以做同样的事情
struct A {
virtual void foo(); //No longer pure virtual
};
void A::foo(){ std::cout << "Default foo" << std::endl; }
struct B : A{
virtual void foo(){ A::foo(); std::cout << "Derived foo" << std::endl; }
};
如果我不定义 impure 虚函数如下:
struct A {
virtual void foo();
};
struct B : A{
virtual void foo(){ std::cout << "Derived foo" << std::endl; }
};
它会工作得很好。
那么,纯虚函数的唯一目的是使类抽象(不可实例化)?
【问题讨论】:
-
选择所谓的默认实现还是需要派生类显式这样做的。
-
如果您通过基类引用/指针调用
foo,您的演示doesn't always work。使用 gcc 编译总是会导致链接器错误,而使用 clang 则取决于优化级别。 -
@stella,也不在不纯的虚函数中。如果派生类想要使用基类实现,它可以简单地不提供覆盖函数。这并不是说它可以隐式使用基类实现作为自己实现的part,而是选择默认实现,我的意思是整个实现。
-
“我们仍然可以像我在示例中所做的那样为纯虚函数提供默认实现,并通过...调用它” - 如果它是“默认实现”你不需要明确地调用它。它只是 一个 实现——C++ 方便地让您使用相同的函数名称,因此它可以向编写派生类的开发人员暗示它可能是他们自己的实现的有用支持函数,但它不会如果基类不能实现纯虚拟并且可能提供不同命名的支持函数,那么会有任何真正的不同。
-
您的最后一个示例导致未定义的行为(无需诊断):由于
A::foo()未标记为= 0;,因此它必须在某处有一个主体(即使它从未被调用)。
标签: c++ inheritance definition virtual-functions one-definition-rule