struct CA{
CA(){ CA::foo(); } // calling foo() without CA is wrong
virtual int foo(){}
};
struct CB {
CB(){ CB::foo(); } // calling foo() without CB is wrong
virtual int foo();
};
Я не вижу никакой заметной разницы между этими двумя классами, принимая во внимание, что, не чисто виртуальная функция должна быть определена в программе, должно быть определение CB::foo
где-то, и это на самом деле не имеет значения, если он определяется внутри определения класса или где-то еще.
В любом случае звонок в foo()
(без явной квалификации) составляет Неверный. Разница между использованием foo()
и Type::foo()
является то, что в первом случае вы используете динамическую диспетчеризацию, что в конечном итоге вызова окончательных подмены функции в это время, в то время как в квалифицированном случае (Type::foo
) дополнительная квалификация препятствует динамическую диспетчеризации ,
Важно отметить на данный момент. Динамический тип объекта изменяется во время построения в иерархии типов. При оценке базового конструктора динамический тип равен base
, и, таким образом, конечный переопределятель может быть выбран только из этого класса. После завершения базового конструктора динамический тип изменяется на следующий тип в иерархии, а конечный перехватчик может быть выбран из этих двух типов и так далее.
struct base {
base() { foo() };
virtual void foo() { std::cout << "base\n"; }
};
struct d1 : base {
d1() : base() { foo() };
};
struct d2 : d1 {
d2() : d1() { foo() };
virtual void foo() { std::cout << "d2\n"; }
};
Строительство объекта типа d2
начнется с построением base
субобъекта, то foo()
вызова будет послан к окончательным подменам, которая в настоящее время только рассматривает base
типа и печатает "base"
. Затем выполняется конструктор d1
, который вызывает foo()
, выбирая конечный переулок на этом уровне, который по-прежнему base::foo()
. После этого вычисляется конструктор d2
. На данный момент окончательных подмен являются d2::foo()
и "d2"
распечатываются получают:
base
base
d2
Обратите внимание, что даже думал, что он только полный объект, который мы создаем имеет типа d2
, он вел себя временно в качестве base
объекта, а затем в качестве d1
объект, и только когда конструктор d2
начинает выполнение, он начинает вести себя как объект d2
и вызывает d2
. Это считается запутанным многими.
Обратите внимание, что поскольку динамический тип объекта на каждом уровне - это точно тип оцениваемого конструктора, конечный переадресатор всегда будет отправлен на этот тип. Добавление дополнительной квалификации не делает большой разницы, за исключением, когда имеем дело с ...
чисто виртуальных функций
Чистые виртуальные функции могут иметь определение. Чистый классификатор означает, что производные типы должны предоставлять определение этой виртуальной функции, а также означает, что чистая виртуальная функция (если она определена) никогда не будет вызываться при динамической отправке. В этом конкретном случае, дополнительная квалификация, которая отключает динамическую диспетчеризацию может быть использована для вызова этой конкретной реализации:
struct base {
base() { base::foo(); } // [*]
virtual void foo() = 0;
};
void base::foo() { std::cout << "base\n"; }
struct derived : base {
derived() : base() { foo(); base::foo(); }
virtual void foo() { std::cout << "derived\n"; }
};
Это единственный случай, когда неквалифицированный вызов foo
в [*]
имеет неверный формат, как и когда этот конструктор оценивается foo
является чистым виртуальным и нет окончательный переулок. С другой стороны, добавление квалификации отключает динамическую отправку и код выше, будет компилироваться и запускаться. Также обратите внимание, что в конструкторе derived
вы можете использовать дополнительную квалификацию, чтобы выбрать конкретное определение функции base::foo
, которая не может быть конечным переучителем на этом уровне. Код по изображению:
base
derived
base
Зачем вам это нужно или вам просто интересно? – 2013-03-07 13:32:39
С некоторыми компиляторами это не так, но вызывает только его собственное foo, то есть не ведет себя как виртуальный fn в конструкторе. – QuentinUK
Вам не нужен явный оператор разрешения области: компилятор делает это за вас. Фактически, компилятор, делающий это, является причиной того, что вызов виртуальных машин из конструкторов на C++ сам по себе не является плохим (в отличие от других языков, где эта практика намного менее безопасна). – dasblinkenlight