C++中模板结构体派生类的静态成员为何未隐藏基类同名静态成员?
解答你的C++静态成员与多态疑问
咱们来一步步拆解你遇到的这些问题,核心原因都绕不开静态成员的访问规则和静态成员不参与多态这两个关键点:
1. 为什么直接访问testval和调用get_testval()结果不同?
首先明确一个核心规则:静态成员是属于类的,不属于任何对象。当你通过指针访问静态成员时,编译器只看指针的静态类型(也就是你声明指针时的类型),完全不管指针实际指向的对象是什么类型。
在你的代码里,s.DERIVED_TYPES是vector<unique_ptr<BASE>>,所以所有元素的静态类型都是BASE*:
- 当你写
s.DERIVED_TYPES[0]->testval时,编译器会直接解析为访问S::BASE::testval(初始值0),根本不会管这个指针实际指向的是DERIVED_01对象。 - 而
get_testval()是虚函数,虚函数的调用是动态绑定的——程序会根据指针指向的实际对象类型,调用对应的派生类实现。DERIVED_01继承自DerivedTemplate<DERIVED_01>,它的get_testval()返回的是自己的静态成员DerivedTemplate<DERIVED_01>::testval(初始值1),所以结果和直接访问不同。
2. 为什么给不同基类指针的testval赋值后,所有输出都是最后一次赋值的值?
还是同一个原因:所有基类指针的静态类型都是BASE*,所以不管你用哪个指针执行->testval = xxx,本质上都是在给S::BASE::testval赋值。最后一次赋值是2.75,所以所有直接访问->testval的地方,显示的都是这个基类静态成员的值——它们访问的是同一个变量!
3. 为什么DerivedTemplate<T>的静态成员没隐藏基类的同名静态成员?
其实不是没隐藏,而是你访问的方式不对。派生类的静态成员确实会隐藏基类的同名静态成员,但这个隐藏效果只有在通过派生类类型的指针/对象访问时才会体现。
比如,如果你修改代码,用派生类类型的指针访问:
// 尝试将基类指针转为派生类指针 auto d01_ptr = dynamic_cast<S::DERIVED_01*>(s.DERIVED_TYPES[0].get()); if (d01_ptr) { cout << d01_ptr->testval << endl; // 这里会输出DerivedTemplate<DERIVED_01>::testval的值 }
这时候就能访问到派生类自己的静态成员了,因为此时指针的静态类型是DERIVED_01*,编译器会优先选择派生类的静态成员。
4. 最后赋值后,直接访问testval显示的是BASE赋值的5.5?
再看这段代码:
s.DERIVED_TYPES[0]->set_testval(3.5); // 调用DerivedTemplate<DERIVED_01>::set_testval,修改自己的testval s.DERIVED_TYPES[1]->set_testval(4.5); // 同理,修改DerivedTemplate<DERIVED_02>::testval s.DERIVED_TYPES[2]->set_testval(5.5); // 调用BASE::set_testval,修改S::BASE::testval s.DERIVED_TYPES[3]->set_testval(6.5); // 修改DerivedTemplate<DERIVED_03>::testval
当你直接用基类指针访问testval时,还是访问S::BASE::testval,所以显示的是5.5;而get_testval()是动态绑定,会调用对应对象的派生类实现,返回各自DerivedTemplate<T>::testval的值,所以和直接访问结果不同。
总结关键知识点
- 静态成员属于类,访问时由指针/引用的静态类型决定,不参与多态,和对象实际类型无关。
- 虚函数是基于对象实际类型的动态绑定,能正确调用到派生类的实现。
- 派生类的静态成员会隐藏基类同名成员,但必须通过派生类类型访问才能体现隐藏效果。
内容的提问来源于stack exchange,提问作者Edmond
相关产品推荐
相关产品推荐

