2013-03-07 3 views
2

В C++ мы не должны вызывать виртуальные функции конструкторов/деструкторов.Вызов виртуальных функций внутри конструктора классов

Что делать, если я явно называю эти функции областью класса?

По существу, мы говорим, что мы не хотим получать прибыль от виртуальности.

Правильный ли псевдокод?

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(); 
} 
+0

Зачем вам это нужно или вам просто интересно? – 2013-03-07 13:32:39

+0

С некоторыми компиляторами это не так, но вызывает только его собственное foo, то есть не ведет себя как виртуальный fn в конструкторе. – QuentinUK

+1

Вам не нужен явный оператор разрешения области: компилятор делает это за вас. Фактически, компилятор, делающий это, является причиной того, что вызов виртуальных машин из конструкторов на C++ сам по себе не является плохим (в отличие от других языков, где эта практика намного менее безопасна). – dasblinkenlight

ответ

1

Вызов CB::foo() не динамически отправляется, поэтому нет никаких несчастных сюрпризов, ожидающих вас или читающий этот код. Это прекрасно. На самом деле было бы также просто позвонить foo(), так как это идентично стандарту в этом сценарии, хотя вы можете оставить комментарий для своих сотрудников.

+0

+1 Почему вы оставили комментарий для своих коллег? * Не * оставляя этот комментарий хорошим способом научить их чему-то новому ;-) – dasblinkenlight

+0

@dasblinkenlight: Если вы хотите научить своих коллег, поговорите с ними. Источник - это неправильное место для обучения, ясность здесь имеет первостепенное значение. – DevSolar

+0

@DevSolar Ну, это был извращенный комментарий, направленный на то, чтобы подчеркнуть разницу между «обучением их» и «обучением их уроку» :) – dasblinkenlight

1
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 
Смежные вопросы