继承场景下pImpl调用疑问:B调用A的foo()时使用哪个impl?
问题解答:调用B.foo()时会使用哪个impl?
嘿,这个问题刚好戳中了C里名字查找规则的关键点,结合《Effective C》第三版里的隐藏概念,咱们一步步理清楚:
首先明确你的场景:基类A定义了foo(),子类B继承A且没重写foo();A的foo()里会调用impl->foo(),而A和B各自有一个名为impl的成员。现在调用B.foo(),到底会用A::impl还是B::impl?
答案是:会使用A::impl,原因和名字查找的顺序直接相关,《Effective C++》里讲的规则正好能解释:
编译器在处理成员函数里的名字时,查找顺序是这样的:
- 第一步:检查函数的局部作用域(比如
A::foo()里有没有局部变量叫impl); - 第二步:检查包含这个函数的类的作用域(也就是A的类作用域);
- 第三步:沿继承层次向上查找(也就是找A的基类,而不是向下找派生类B)。
当A::foo()里出现impl这个名字时,编译器在第二步就找到了A::impl,这时候查找就直接停止了——根本不会去派生类B的作用域里找同名的impl。毕竟基类A的代码在编译时,完全不知道有B这个子类存在,自然不会去“下游”的子类里找名字。
咱们用一段代码验证一下就更直观了:
#include <iostream> struct ImplBase { virtual void foo() { std::cout << "A的impl的foo\n"; } }; struct AImpl : ImplBase { void foo() override { std::cout << "A的impl的foo\n"; } }; struct BImpl : ImplBase { void foo() override { std::cout << "B的impl的foo\n"; } }; class A { protected: ImplBase* impl = new AImpl(); public: void foo() { impl->foo(); } }; class B : public A { protected: ImplBase* impl = new BImpl(); }; int main() { B b; b.foo(); // 输出:A的impl的foo return 0; }
运行这段代码,你会看到输出的是A的impl的foo,完全符合咱们的结论。
如果想要让B.foo()使用B::impl,那得换个思路——比如让A提供一个虚函数来返回impl,让B重写这个虚函数:
class A { protected: virtual ImplBase* get_impl() { return new AImpl(); } public: void foo() { get_impl()->foo(); } }; class B : public A { protected: ImplBase* get_impl() override { return new BImpl(); } };
这样调用B.foo()时,就会调用B的get_impl(),从而使用B::impl的foo()了。
内容的提问来源于stack exchange,提问作者SomeoneSomewhere
相关产品推荐
相关产品推荐

