虽然通过STL源(DinkumWare,SGI,STLport等)挖掘并尝试理解他们的实现选择(它进展顺利),但我发现了一些我觉得有点奇怪或者我从未跑过的东西进入之前。
通常,当希望重载派生类中的成员函数时,您将使用virtual关键字预先添加基类成员函数签名,但是在STL源的各个点处,情况并非如此。
这是我在STL实现中看到的缩减版本:
template <typename T> class A {
public:
void func( ) { std::cout << "inside A func( )" << std::endl; }
};
template <typename T> class B : public A<T> {
public:
void func( ) { std::cout << "inside B func( )" << std::endl; }
};
编译器似乎很好用这个伪多态,因为我期待一个错误的东西:
error C2535: 'void B<T>::func(void)': member function already defined or declared
有人会善意地解释这里发生了什么吗?
PS:这似乎也可以在没有类模板的情况下工作。
“的问候
答案 0 :(得分:2)
B<T>::func
成员只需shadows A<T>::func
。当您致电p->func()
A<T> *p
指向B<T>
时,会调用A<T>::func
,因此没有多态性。
#include <iostream>
struct A
{
void func() { std::cout << "Hello!\n"; }
};
struct B : public A
{
void func() { std::cout << "Goodbye!\n"; }
};
int main()
{
B b;
A *p = &b;
p->func();
b.func();
}
(Demo)
在C ++标准中,至少有一个地方可以利用此隐藏/名称隐藏:std::ifstream::rdbuf
hides its ancestor's method by that name并实际更改其返回类型。
答案 1 :(得分:2)
没有virtual
关键字 - 重新定义函数时,您隐藏了超级函数。
在您的情况下,通过重新定义func()
,您告诉编译器B
有一个新函数,它与A
不同。
但是,因为它未声明为virtual
,所以只有从type func()
变量调用B
时才会看到此影响。保存A
的{{1}}类型的变量将调用B
的func()。
A
将调用第一个[A *a = new B;
a->func()
]方法。
要调用A
的方法,您需要类型为B
:
B
答案 2 :(得分:1)
显然没有错误,因为这些函数只是重载:A::func()
有一个签名,将A
对象(引用或指针)作为第一个参数,而B::func()
有签名将B
对象作为第一个参数。也就是说,这只是使用不同参数但函数名称重载两个函数。
这是在一些地方完成的,以便从一个函数产生不同的返回类型,这个函数基本上很容易转发到另一个函数(至少,这些是我能想到的地方)。这只是为了让用户的生活更轻松,尽管它实际上比其他任何东西都更容易混淆。我能想到的例子(例如流中的rdbuf()
函数)应该创建一个不同的名称。
答案 3 :(得分:1)
这是可接受的代码,B<T>::func
只是隐藏A<T>::func
。
A<int> a;
B<int> b;
a.func(); // inside A
b.func(); // inside B
A<int> *const pA = new B<int>();
pA->func(); // inside A
通过多态类型调用func
时,它会调用指针类型的函数。