重写虚函数时语法怪异
Weird syntax when overriding virtual functions
读到这个答案,我很惊讶地自己尝试一下,如果它真的像描述的那样工作:
#include <iostream>
class A {
public:
virtual void foo() = 0;
};
class B {
public:
virtual void foo() = 0;
};
class C : public A, public B {
public:
virtual void foo();
};
void C::foo(){
std::cout << "C" << std::endl;
}
void C::A::foo(){
std::cout << "A" << std::endl;
}
void C::B::foo(){
std::cout << "B" << std::endl;
}
int main() {
C c;
static_cast<A*>(&c)->foo();
static_cast<B*>(&c)->foo();
c.foo();
return 0;
}
我真的不认为可以从两个具有相同名称和签名的不同基类重写虚方法。并且,正如我所料,上面的程序打印:
C
C
C
所以答案是错误的——正如我从一开始就感觉到的那样。令人惊讶的部分是:为什么我的gcc接受这种语法:void C::A::foo(){
?我找不到任何关于这可能意味着什么的东西。这是一个gcc bug/特性吗?是一些晦涩的标准c++语法吗?还是我完全误解了形势?
在这种情况下,void C::A::foo(){}
似乎只是A::foo
的定义。但是为什么呢?这是GCC bug吗?或者这是标准所允许的?如果是这样,它是针对这种事情的特定规则,还是某种泛型子句(例如:如果标识符没有意义-例如'C::A::foo',那么编译器可以自由地做它想做的事情)。
我认为C::A::foo
定义A::foo
的事实是由于注入了class-name。
N3337 [class]/2:
在类名被看到之后,类名被插入到声明它的作用域中。类名也被插入到类本身的作用域中;这就是被注入的类名。出于访问检查的目的,注入的类名被视为公共成员名。[…]
由于A
是C
的基类,并且A
的名字被注入到A
的作用域中,所以A
在C
中也是可见的。
鉴于上述情况,我们可以做一些可怕的反常的事情,如:
void C::C::C::A::A::A::foo(){
std::cout << "A" << std::endl;
}
现场演示
有趣的是,
void C::A::foo(){
std::cout << "A" << std::endl;
}
定义了A::foo()
(如果愿意,您可以提供纯虚函数的实现)。您可以通过添加一个附加定义来检查是否确实如此:
void A::foo(){
std::cout << "Base A" << std::endl;
}
void C::A::foo(){
std::cout << "A" << std::endl;
}
GCC将报告一个多重定义错误。这可能是有意义的,因为您可以将C::A
视为A
的名称别名。
就标准所说的而言,我目前正在努力寻找在标准海洋中何时合适的确切定义,但我可以在第3.4.3.1.2段中看到一个相关的例子:
struct A { A(); };
struct B: public A { B(); };
A::A() { }
B::B() { }
B::A ba; // object of type A // <-- here
A::A a; // error, A::A is not a type name
struct A::A a2; // object of type A
这个例子与构造函数解析有关,但是构造函数最终是一个方法,所以我猜GCC对每个作用域解析都使用了相同的机制。
Visual Studio编译器(2008)不允许
void C::A::foo(){
std::cout << "A" << std::endl;
}
void C::B::foo(){
std::cout << "B" << std::endl;
但只能
void A::foo(){
std::cout << "A" << std::endl;
}
void B::foo(){
std::cout << "B" << std::endl;
为纯虚方法提供定义是完全有效的。
"Effective c++"Meyers提到了使用纯虚函数的原因实现这个纯虚函数的派生类函数可以在其代码中的某处调用此实现。如果部分两个不同的派生类的代码是相似的在层次结构中向上移动它是有意义的,即使函数应该是纯虚拟。
请看这里(forum.codeguru.com)
纯虚函数不能动态调用,只能静态调用:
C c;
static_cast<A*>(&c)->foo(); // prints C
static_cast<B*>(&c)->foo(); // prints C, dynamic dispatch
c.foo(); // prints C, dynamic dispatch
c.A::foo(); // prints A, static dispatch
c.B::foo(); // prints B, static dispatch
解释:
当你使用虚函数的全限定名类名后面跟着"::"),编译器不使用虚函数调用机制,而是使用与调用相同的机制非虚拟函数。换句话说,它按名称调用函数而不是通过槽号。如果你想在派生类里写代码调用Base::f(),即在其基中定义的f()的版本类的基础,你应该写:
void Derived::f()
{ Base::f(); }
请看这里(isocpp.org)
- QMetaObject invokeMethod的基于函数指针的语法
- 使用基类指针调用基类的值构造函数的语法是什么?
- C++使用 rand 定义函数语法
- C++中未命名函数指针的语法
- 将值传递给构造函数 c++ 的差异语法
- C++语法中的函数指针
- 存储函数指针的正确语法
- QObject::连接不起作用 - 使用函数语法找不到信号
- 对函数库中的语法感到困惑 std::bind
- 解释通过从函数引用返回数组的语法
- 函数错误 C2059:语法错误:'>'不起作用
- 当C++类函数参数之一是结构时,它们的语法有什么不同
- 将显式指定的函数模板重载作为模板参数传递的正确语法是什么?
- Qt的新信号/时隙语法问题 - 连接到一个简单的函数
- 需要"模板<>"语法 --> 通过函数调用类模板
- C++ std::函数语法问题
- 备用函数语法/函数原型
- 奇怪的c++语法:函数调用之前的类型定义
- c++数组语法(函数返回数组)
- 实现[B,C]=f(A)语法(函数f作用于具有两个或多个输出数组的数组)