Это связано с тем, что использование объявления не вводит новый член или новое определение. Скорее, он вводит набор объявлений, которые можно найти с помощью поиска по полному имени [namespace.udecl]/1:
Каждый декларатор использования в объявлении использования вводит набор объявлений в декларативную область, в которой появляется объявление использования. Набор объявлений, введенных декларатором использования, находится путем поиска уточненного имени ([basic.lookup.qual], [class.member.lookup]) для имени в деклараторе использования, исключая функции, которые скрыты, как описано ниже.
Это влияет только на сущность(и), найденную(ые) при поиске по уточненному имени. Таким образом, он не влияет на определение последнего переопределения [class.virtual]/2:
[...] Виртуальная функция-член C::vf объекта класса S является окончательным переопределением, если только самый производный класс ([intro.object]), из которого S не является подобъектом базового класса (если таковой имеется) объявляет или наследует другую функцию-член, которая переопределяет vf.
Что имеет другое значение, чем: конечный переопределяющий объект — это объект, обозначенный выражением D::vf, где D — наиболее производный класс, из которого S — подобъект базового класса.
И, как следствие, это не влияет на то, является ли класс абстрактным классом [class.abstract] /4а>:
Класс является абстрактным, если он содержит или наследует хотя бы одну чисто виртуальную функцию, для которой окончательный переопределяющий класс является чисто виртуальным.
Примечание 1:
Следствием этого является то, что использование директивы приведет к различному поведению для не виртуальных и виртуальных функций [expr.call]/3:
Если выбранная функция не является виртуальной или если id-выражение в выражении доступа к члену класса является квалифицированным id, вызывается эта функция. В противном случае вызывается его окончательный переопределитель в динамическом типе объектного выражения; такой вызов называется вызовом виртуальной функции.
Просто:
- не виртуальная функция => функция, найденная при поиске квалифицированного имени
- виртуальная функция => вызов окончательного переопределения
Итак, если print
не был виртуальным:
class A {
public :
void print() {
std::cout << "\n Class A::print()";
}
};
int main() {
B b;
C c;
b.print() // Class B print ()
c.print() // Class A print ()
//Equivalent to:
c.C::print() // Class A::print()
return 0;
}
Заметка 2:
Как некоторые могли заметить в предыдущем стандартном абзаце, можно выполнить квалифицированный вызов виртуальной функции, чтобы получить невиртуальное поведение. Таким образом, использование объявления виртуальной функции может быть практичным (вероятно, плохой практикой):
class A {
public :
virtual void print() =0;
};
//Warning arcane: A definition can be provided for pure virtual function
//which is only callable throw qualified name look up. Usualy an attempt
//to call a pure virtual function through qualified name look-up result
//in a link time error (that error message is welcome).
void A::print(){
std::cout << "pure virtual A::print() called!!" << std::endl;
}
int main() {
B b;
C c;
b.print() // Class B print ()
c.print() // Class B print ()
c.C::print() // pure virtual A::print() called!!
//whitout the using declaration this last call would have print "Class B print()"
return 0;
}
Текущая демонстрация
person
Oliv
schedule
04.01.2019