我最近遇到了以下类型的代码:
struct A {
virtual void foo() = 0;
};
struct B : public A {
virtual void foo() = 0;
};
struct C : public B {
virtual void foo() override {
}
};
重新声明纯虚拟方法是否有目的?
更重要的是:这会改变语义吗?即B::foo
是否在A::foo
的阴影下并引入了新的vftable?
如果A::foo
实际上不是纯虚拟的,但提供了一个实现(仍为我编译),在上面的示例中会发生什么?
答案 0 :(得分:6)
请看cppreference中的示例:
struct Abstract { virtual void f() = 0; // pure virtual }; // "Abstract" is abstract struct Concrete : Abstract { void f() override {} // non-pure virtual virtual void g(); // non-pure virtual }; // "Concrete" is non-abstract struct Abstract2 : Concrete { void g() override = 0; // pure virtual overrider }; // "Abstract2" is abstract int main() { // Abstract a; // Error: abstract class Concrete b; // OK Abstract& a = b; // OK to reference abstract base a.f(); // virtual dispatch to Concrete::f() // Abstract2 a2; // Error: abstract class (final overrider of g() is pure) }
重新声明纯虚拟方法是否有目的?
有一个目的是在派生类中将虚拟方法声明为纯方法:Abstract2
可以将g
降级为纯虚拟方法,即使它在Concrete
中不是纯方法。
这会改变语义吗?即是B :: foo遮盖A :: foo并引入新的 vftable?
否。
如果A :: foo实际上不是纯虚拟的,但提供了一个实现(仍然为我编译),在上面的示例中发生了什么?
请参见示例。
您不必在派生类中将方法像virtual
那样传递。当在基中将其声明为virtual
时,它在派生类中也为virtual
。简而言之,=0
只是告诉编译器不一定有定义,并且该类是抽象的。您的示例与以下示例相同:
struct AA { // abstract
virtual void foo() = 0;
};
struct BB : public AA { }; // abstract
struct CC : public BB { // concrete
void foo() override {}
};
我知道的唯一区别是BB::foo
的类型:
// above
std::cout << std::is_same< decltype(&BB::foo), void (AA::*)()>::value;
std::cout << std::is_same< decltype(&BB::foo), void (BB::*)()>::value;
std::cout << std::is_same< decltype(&BB::foo), void (CC::*)()>::value << "\n";
// your example
std::cout << std::is_same< decltype(&B::foo), void (A::*)()>::value;
std::cout << std::is_same< decltype(&B::foo), void (B::*)()>::value;
std::cout << std::is_same< decltype(&B::foo), void (C::*)()>::value << "\n";
输出为:
100
010
也就是说,在foo
中重新声明B
会在foo
中直接引入名称B
。 B::foo
是B
的成员函数。另一方面,在我的示例中,BB::foo
是AA
中的方法。但是,这不会影响C::foo
。
答案 1 :(得分:0)
我发现了一个需要重新声明纯虚方法的例子。 gcc 10 和 clang 11 当纯虚方法用于后代类本身抽象的实现时都需要重复声明。这是一个例子:
class Transition {
public:
virtual int* apply(int*p, double*e) = 0;
virtual long* apply(double*h, double*e) = 0;
virtual long* apply(long* l, double *e) { return l;}
};
class MTransition : public Transition {
public:
//virtual int* apply(int*p, double*e) = 0; // duplicate from upper class but required
virtual long* apply(double*h, double*e) {
int a= *(Transition::apply((int*)h, e)); // not resolved correctly without the duplicate declaration
return a?nullptr:nullptr;
}
};
class TransitionTest : public MTransition {
public:
virtual int* apply(int*p, double*e) { return p; }
};
int main() {
TransitionTest t;
double /*d1=1.0,*/ d2=2.0;
int i1=1;
/*int *pi = t.apply(&d1, &d2); */
int *pi = t.apply(&i1, &d2);
return pi?(*pi):0;
}
因此,此程序不链接。我收到以下错误:
clang++ -Wall yo.cpp
yo.cpp:19:16: warning: 'TransitionTest::apply' hides overloaded virtual function [-Woverloaded-virtual]
virtual int* apply(int*p, double*e) { return p; }
^
yo.cpp:11:17: note: hidden overloaded virtual function 'MTransition::apply' declared here: type mismatch at 1st parameter ('double *' vs 'int *')
virtual long* apply(double*h, double*e) {
^
1 warning generated.
/usr/bin/ld: /tmp/yo-cb0979.o: in function `MTransition::apply(double*, double*)':
yo.cpp:(.text._ZN11MTransition5applyEPdS0_[_ZN11MTransition5applyEPdS0_]+0x27): undefined reference to `Transition::apply(int*, double*)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
如果我尝试在 main 中对 apply
进行其他调用,情况会更糟(在这种情况下是编译错误,与警告相关联)。
为了使它工作,我需要像这样编写 MTransition
类:
class MTransition : public Transition {
public:
virtual int* apply(int*p, double*e) = 0; // duplicate from upper class but required
virtual long* apply(double*h, double*e) {
int a= *(apply((int*)h, e)); // not resolved correctly without the duplicate declaration
return a?nullptr:nullptr;
}
};
但是我没有成功在 main 中调用 t.apply(&d1, &d2);
。在我看来,apply(int*p, double*e)
中 TransitionTest
的定义隐藏了(如警告所述)本应继承的另一个 apply
。
编辑(再次):我在 c++ overloaded virtual function warning by clang? 中找到了解决方案
当重载(或部分覆盖)派生类中的方法时,需要使用 using Base::method
来保留其他重载的方法。
它给出:
class Transition {
public:
virtual int* apply(int*p, double*e) = 0;
virtual int* apply(double*h, double*e) = 0;
virtual int* apply(long* l, double *e) { std::cerr << "Transition::apply(long*,double*)" << std::endl; return nullptr;}
};
class MTransition : public Transition {
public:
using Transition::apply;
virtual int* apply(double*h, double*e) {
std::cerr << "MTransition::apply(double*,double*)" << std::endl;
int a= *(apply((int*)h, e)); // not resolved correctly without the duplicate declaration
return a?nullptr:nullptr;
}
};
class TransitionTest : public MTransition {
public:
using MTransition::apply;
virtual int* apply(int*p, double*e) { std::cerr << "TransitionTest::apply(int*,double*)" << std::endl; return p; }
};
int main() {
TransitionTest t;
double d1=1.0, d2=2.0;
//int i1=1;
int *pi = t.apply(&d1, &d2);
//int *pi = t.apply(&i1, &d2);
return pi?(*pi):0;
}