Наследование, псевдополиморфизм - PullRequest
1 голос
/ 11 февраля 2012

Пока копаясь в источниках STL (DinkumWare, SGI, STLport и т. Д.) И пытаясь понять их варианты реализации (все идет хорошо), я наткнулся на то, что мне кажется немного странным или, скорее, я никогда не запускался в до.

Обычно, когда кто-то желает перегрузить функцию-член в производном классе, вы должны добавить в подпись функции-члена базового класса виртуальное ключевое слово, однако в разных точках источника 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: Это также работает без шаблонов.

Привет

Ответы [ 4 ]

2 голосов
/ 11 февраля 2012

Без ключевого слова virtual - при переопределении функции вы скрываете функцию супер.

В вашем случае путем повторного определения func() вы сообщаете компилятору, что есть новая функция для B, которая отличается от A.

Хотя, поскольку он не объявлен virtual, вы увидите, что этот эффект будет только в том случае, если вы вызовете func() из переменной type B.Переменная типа A, которая содержит B, вызовет func () A.

A *a = new B;
a->func()

вызовет первый метод [A].

Чтобы вызвать метод B, необходимо, чтобы тип был B:

B *b = new B;
b->func()
2 голосов
/ 11 февраля 2012

Член B<T>::func просто тени 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 скрывает метод своего предка под этим именем и фактически меняет свой тип возвращаемого значения.

1 голос
/ 11 февраля 2012

Это приемлемый код, 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 через полиморфный тип он вызовет функцию типа указателя.

1 голос
/ 11 февраля 2012

Здесь явно нет ошибки, потому что эти функции являются просто перегрузками: A::func() имеет подпись, принимающую объект A (ссылку или указатель) в качестве первого аргумента, в то время как B::func() имеет подпись, принимающую объект Bв качестве первого аргумента.То есть, это просто перегрузка двух функций с разными аргументами, но с именем функции.

Это делается в нескольких местах для получения другого типа возврата из функции, которая по существу тривиально пересылается в другую функцию (впо крайней мере, это те места, о которых я могу думать).Это просто, чтобы сделать жизнь немного проще для пользователей, хотя это на самом деле более запутанно, чем все остальное.Примеры, которые я могу придумать (например, функция rdbuf() в потоках) должны были создать другое имя.

...