#cpp #ооп #классы #виртуальная_функция
Почему выводит 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(); }
Ответы
Ответ 1
Потому что значения аргументов по умолчанию обрабатываются при компиляции, а виртуальная диспетчеризация - во время выполнения. Что видит компилятор? вызов foo() для типа A - и понимает, что нужно подставить значение аргумента по умолчанию (для типа A это A). При вызове вызывается B::foo() с аргументом, полученным при компиляции - а именно, A. Вот представьте, что в каком-то файле будет какой-то опосредованный потомок с еще каким-то аргументом по умолчанию - как вообще компилятор сможет его найти? Это что, компилятор в такой ситуации должен хранить кроме таблицы виртуальных функций еще и таблицу значений аргументов по умолчанию (причем для тех потомков, которых во время компиляции еще и в проекте нет)?Ответ 2
Всё просто на самом деле. В этом месте 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 A virtual function call (13.3) uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default arguments from the function it overrides.
Комментариев нет:
Отправить комментарий