Почему выводит A A
, а не A B
?
struct A {
virtual void foo(char x = 'A'){
std::cout << x << ' ';
};
};
struct B : A {
void foo(char x = 'B') override {
A::foo(x);
}
};
int main() {
A* a = new A{};
A* b = new B{};
a->foo();
b->foo();
}
Потому что значения аргументов по умолчанию обрабатываются при компиляции, а виртуальная диспетчеризация - во время выполнения.
Что видит компилятор? вызов foo()
для типа A
- и понимает, что нужно подставить значение аргумента по умолчанию (для типа A
это A
).
При вызове вызывается B::foo()
с аргументом, полученным при компиляции - а именно, A
.
Вот представьте, что в каком-то файле будет какой-то опосредованный потомок с еще каким-то аргументом по умолчанию - как вообще компилятор сможет его найти? Это что, компилятор в такой ситуации должен хранить кроме таблицы виртуальных функций еще и таблицу значений аргументов по умолчанию (причем для тех потомков, которых во время компиляции еще и в проекте нет)?
Всё просто на самом деле.
В этом месте b->foo();
компилятор должен проверить и построить легальный код для вызова. Он прекрасно видит, что есть один вариант foo
, который можно использовать таким образом (вызов без указания параметра), и ему нужно здесь передать в функцию аргумент: b->foo(параметр-заданный-по-умолчанию);
. Внимание, у нас этап компиляции, и компилятору неизвестно объект какого типа реально окажется в памяти. Поэтому сначала кратенько напомню, что в C++ имеются статический и динамический типы.
Динамический тип - это тип того объекта, который непосредственно будет создан и лежать в памяти, т.е. в нашем случае это объект типа B
.
Статический тип - это тип выражения, который определяется в результате анализа программы без учета выполнения семантики. В нашем случае в выражении b->foo();
b
- это указатель на объект типа A
.
То, что в памяти при b->foo();
окажется объект типа B
будет известно только во время выполнения, в общем случае, во время компиляции компилятору неизвестно какой реально тип имеет объект, на который указывает b
, но код вызова b->foo();
он построить должен уже сейчас, так что у него нет другого выбора, кроме как взять параметр по-умолчанию, исходя из статического типа, и поставить его в аргументы, т.е. параметр будет взят из A::foo
, и будет построен код аналогичный b->foo('A');
.
Именно об этом говорит пункт 11.3.6/10 (N4659) стандарта C++:
11.3.6 Default arguments
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
пишу сервис который убивает приложение Google Chrome, основная задача в том, как отловить запуск Google Chrome? Можно сделать циклом в отдельном потоке,...
Я парсил сайт, все спаршиные данные я перевёл в строкиТеперь я должен поместить эти строки в таблицу
Я хочу при запуске программы через cmd сразу передать ей нужные аргументы для дальнейшей работы с нимиТо есть, пишу что-то типа C:>java Main arg1 arg2