子类中调用被覆盖的父类函数必须显式使用作用域解析符Base::func(),因C++不会自动向上查找;多继承时需指定基类作用域以消除歧义;虚继承不影响语法但需注意初始化顺序;模板类中依赖型调用需加this->或Base::前缀。

子类中直接调用被覆盖的父类函数
当子类重写了父类的虚函数,又需要在子类实现里复用父类逻辑时,必须显式限定作用域。C++ 不会自动“向上查找”被覆盖的版本,func() 默认调用当前类型的版本。
正确写法是用作用域解析符:Base::func()。这和是否虚函数无关,只要函数名被子类声明过(哪怕只是同名非虚函数),不加限定就会调用子类版本。
- 如果父类函数是
virtual,而子类用override重写,Base::func()仍能安全调用父类原始实现 - 若父类函数非虚,子类中同名函数会隐藏(hide)而非覆盖(override)所有基类重载,此时更需用
Base::func()显式调用 - 不能写成
this->Base::func()——Base::func()本身已是合法表达式,加this->反而可能触发意外的 ADL 或模板推导问题
多继承时调用特定父类的同名函数
多个基类都定义了同名函数(无论是否虚),子类中直接写 func() 会编译失败:调用有歧义。这时必须明确指定基类作用域。
例如类 D 同时继承 B1 和 B2,二者都有 void print(),则 D::print() 内部若想调用 B1 版本,必须写 B1::print();调用 B2 版本则写 B2::print()。
立即学习“C++免费学习笔记(深入)”;
- 即使某基类函数是虚函数,这种显式调用也不会触发动态绑定——
B1::print()就是静态绑定到B1的定义 - 若某基类是虚继承,不影响作用域调用语法,但要注意虚基类构造顺序和初始化责任
- 避免在构造函数中调用尚未完成初始化的基类虚函数,否则行为未定义
通过指针/引用调用父类版本的陷阱
用基类指针或引用调用函数时,是否执行父类版本,取决于对象实际类型和函数是否为虚函数,跟调用语法无关。这不是“调用父类函数”的可靠方式。
常见误解:以为 Base* p = new Derived(); p->func(); 就是在调用父类函数 —— 实际上,如果 func 是虚函数,这仍会调用 Derived::func();只有把函数设为 final 或去掉 virtual 才可能绕过,但这改变了设计语义。
- 真正想执行父类逻辑,唯一可控方式仍是
Base::func()这种显式作用域调用 - 把对象强制转型为父类引用(如
static_cast<base>(*this))再调用,等价于Base::func(),但更冗长且易出错 - 若父类函数非虚,而子类未重写,那么
Base::func()和this->func()效果相同;但一旦子类声明了同名函数,后者就不再指向父类版本
模板类中调用父类成员函数的注意事项
模板派生类里调用父类函数时,若父类是依赖型(dependent),编译器可能无法在第一阶段解析函数名,导致 “not declared in this scope” 错误。
典型场景:父类模板参数未确定,Base<t>::func()</t> 中的 func 被视为依赖名,必须加 this-> 或 Base<t>::</t> 前缀,并在调用前确保它已声明。
- 写
this->func()是最稳妥的选择,它告诉编译器这是依赖型成员访问 - 写
Base<t>::func()</t>也可行,但要求func在Base<t></t>中可见(不能是私有,且不能被子类同名函数完全隐藏) - 若父类函数是模板成员函数,还需补全模板参数,如
Base<t>::template func<int>()</int></t>
C++ 没有“super”关键字,所有向上调用都靠显式作用域限定。最容易被忽略的是:隐藏(hiding)比覆盖(overriding)更常见,也更危险——哪怕父类函数不是虚的,只要子类出现同名声明,就会切断所有基类重载的可见性。


















