2013-03-03 2 views
1

Я понимаю основы перегрузки и переопределения, но что-то меня смущает. Я попытаюсь объяснить, используя простой пример:Как перегрузка и переопределение работают вместе?

  • Класс B имеет функцию X (В & б)
  • Класс D наследует от класса В.
  • Класс D переопределяет X с X (В & б), а также перегружает его с помощью X (D & d).

Я тогда следующий код:

void test(D& d1, B& b1, D& d2, B& b2){ 
    d1.X(d2); 
    d1.X(b2); 
    b1.X(d2); 
    b1.X(b2); 
} 

int main(){ 
    D d1, d2, d3, d4; 
    test(d1, d2, d3, d4); 
} 

я очень уверен, как третья и четыре линии test() бы определить, какие реализации X() называть и то, что общие механизмы, которые являются происходит.

+2

Полиморфизм происходит только с указателями и ссылками; проходящий по значению срезов '' '' '' '' '' '' '' '' '' ''. Также даже если вы исправите это, перегрузка 'D :: X (D)' не будет видна при вызове 'X' через' B & '. –

+0

@SethCarnegie Я исправил свое сообщение, чтобы включить ссылки. По-видимому, b1.X (b2) вызовет DX (B & b) ..... это то, что я не понимаю ... – user997112

+0

'B' не имеет функции' X (D) ', только' D' делает. Также типы аргументов для функций (виртуальные или статические, либо) принимаются статически, а не динамически. –

ответ

1

Существует два этапа: выбор перегрузки (X(B&) против X(D&)) и, как только это будет сделано, найти правильную реализацию выбранной функции. Первое происходит во время компиляции и зависят от статических типов объекта и аргументов, последний происходят во время выполнения и зависит от динамического типа объекта (обратите внимание, что делает не зависит от динамический тип аргументов).

Четыре объекты объявляются следующим образом: d1 и d2 являются D&, поэтому их статического типа является D& и b1 и b2 объявлены как B&, так что их статический тип B&. Статический тип - это то, что вы указали в коде.

Но динамический тип для всех четырех является D, потому что все четыре ссылки фактически относится к объектам, которые вы создали, как D -Объектов в main().

Таким образом, первый шаг, выбор перегрузки: В случае b1.X(b2) и b1.X(d2), есть только один возможный перегрузки, X(B&), так как статический тип B&, а определение класса B имеет только эту одну функцию. Но в случае d1.X(b2) и d1.X(d2) выбор перегрузки основан на определении класса D, поскольку статический тип - D&. Так считаются две перегрузки: X(B&) и X(D&). Когда аргумент равен b2, выбирается первая перегрузка, а когда аргумент равен d2, последняя перегрузка выбирается – на основе статических (= объявленных) типов объектов и аргументов.

Второй шаг, выбрав правильную реализацию выбранной перегрузки. Это происходит во время выполнения и зависит от динамического типа объекта (а не от аргументов). Поэтому в случае b1.X(b2), поскольку динамический тип b1 равен D, он в конечном итоге вызовет D::X(B&). То же самое для b1.X(d2): Перегрузка, выбранная на предыдущем шаге, была X(B&), но выбрана реализация D::X(B&). (D::X(D&) на данный момент не является кандидатом, потому что это будет другая перегрузка, и перегрузка была выбрана на основе статического типа уже). В случае d1.X(b2) и d1.X(d2) выбранные функции такие же, как на первом этапе, D::X(B&) и D::X(D&), поскольку динамический тип объекта совпадает с типом статического типа.

+0

Отличный ответ - вы действительно объяснили это хорошо! – user997112

+0

Итак, чтобы уточнить, мы не будем рассматривать второй этап для первых двух назначений, потому что статический тип и динамический тип вызывающих объектов (D) совпадают. Другими словами, первый этап определяет, какая из статических типов перегрузок вызывается, а второй этап определяет (если динамический тип не соответствует статическому типу), из которого класс в иерархии наследования мы вызываем эту выбранную перегруженную функцию. – user997112

+0

Да, это правильно - или, фактически, второй шаг действительно имеет место, но это ничего не меняет, потому что, как вы говорите, динамический тип такой же, как у статического типа. – jogojapan

2

Вы объявляете виртуальную функцию X в B(B::X), и переопределить X в производном классе D(D::X). Если списки параметров B::X и D::X различны, B::X и D::X считаются разными, D::X не переопределяет B::X, а D::X не является виртуальным (если только вы не объявили его с ключевым словом virtual). Вместо этого D::X скрывает B::X.

#include <iostream> 
using namespace std; 

struct B { 
    virtual void X() { cout << "Class B" << endl; } 
}; 

struct D: B { 
    void X(int) { cout << "Class D" << endl; } 
}; 

int main() { 
    D d; 
    B* pb = &d; 
// d.X(); 
    pb->X(); 
} 

Вы не можете даже назвать d.X(), оно скрыто D::X(int). Но pb->X() в порядке.

Так что в вашем случае:

struct B { 
    virtual void X(B& b) { cout << "Class B" << endl; } 
}; 

struct D: B { 
    void X(B& b) { cout << "Class D" << endl; } 
    void X(D& d) { cout << "Class D" << endl; } 
}; 

D::X скроет B::X. Таким образом, d1.X(d2) и d1.X(b2) в test() не имеют отношения к B::X. И b1.X(d2), и b1.X(b2) в test() позвонит D::X. Хотя B::X невидим в D, но D::X(B&) является виртуальным, независимо от того, объявляете ли вы D::X(B&) с ключевым словом virtual. Компилятор знает, что это виртуальная функция, поэтому вызывается D::X(B&).

EDIT: Больше пояснений на b1.X (b2), B :: X является виртуальной функцией, а D :: X переопределяет ее, поэтому определенно она вызовет D :: X путем динамической привязки. И перегрузка определяется во время компиляции, поэтому она не будет вызывать D :: X (D &).

+0

Последний вопрос «почему не» выглядит как вопрос? @jogojapan – StarPinkER

+0

Думаю, он задал мне вопрос: «Вы понимаете, что я имею в виду?» – user997112

+2

Хорошо, я думаю, что это моя проблема с английским. – StarPinkER

Смежные вопросы