13 Derived classes [class.derived]

13.3 Virtual functions [class.virtual]

[ Note: Виртуальные функции поддерживают динамическое связывание и объектно-ориентированное программирование. ] Класс, который объявляет или наследует виртуальную функцию, называется a .end notepolymorphic class

Если виртуальная функция-членvf объявлена ​​в классе Base и в классеDerived, производная прямо или косвенно отBaseфункции-членаvf с тем же именемparameter-type-list,, cv-qualification и ref-qualifier (или отсутствием того же), что Base​::​vf и объявлено, тогдаDerived​::​vf is также виртуальный (объявлен ли он так или нет) и онoverrides111 Base​::​vf. Для удобства мы говорим, что любая виртуальная функция отменяет сама себя. Виртуальная функция - членC​::​vf объекта классаS является ,final overrider еслиmost derived class из которыхS не является базовым классом подобъектом (если таковые имеются) объявляет или наследует другую функцию - член , который переопределяет vf. В производном классе, если виртуальная функция-член подобъекта базового класса имеет более одного окончательного переопределителя, программа имеет неправильный формат. [Example:

struct A {
  virtual void f();
};
struct B : virtual A {
  virtual void f();
};
struct C : B , virtual A {
  using A::f;
};

void foo() {
  C c;
  c.f();              // calls B​::​f, the final overrider
  c.C::f();           // calls A​::​f because of the using-declaration
}

end example]

[Example:

struct A { virtual void f(); };
struct B : A { };
struct C : A { void f(); };
struct D : B, C { };  // OK: A​::​f and C​::​f are the final overriders
                      // for the B and C subobjects, respectively

end example]

[ Note: Виртуальная функция-член не должна быть видимой для переопределения, например,

struct B {
  virtual void f();
};
struct D : B {
  void f(int);
};
struct D2 : D {
  void f();
};

функцияf(int) в классеD скрывает виртуальную функциюf() в своем базовом классеB;D​::​f(int) это не виртуальная функция. Однакоf() объявленный в классе D2 имеет то же имя и тот же список параметров B​::​f(), что и, и, следовательно, является виртуальной функцией, которая переопределяет функцию,B​::​f() даже еслиB​::​f() она не видна в классеD2. ]end note

Если виртуальная функцияf в каком-либо классеB отмечена значками virt-specifierfinal и в классе,D производном от переопределенияB функции , программа имеет неправильный формат. [D​::​f B​::​fExample:

struct B {
  virtual void f() const final;
};

struct D : B {
  void f() const;     // error: D​::​f attempts to override final B​::​f
};

end example]

Если виртуальная функция отмечена знаком virt-specifieroverride и не переопределяет функцию-член базового класса, программа имеет неправильный формат. [Example:

struct B {
  virtual void f(int);
};

struct D : B {
  virtual void f(long) override;  // error: wrong signature overriding B​::​f
  virtual void f(int) override;   // OK
};

end example]

Несмотря на то, что деструкторы не наследуются, деструктор в производном классе переопределяет деструктор базового класса, объявленный виртуальным; видеть[class.dtor] и[class.free].

Тип возврата замещающей функции должен быть идентичен типу возврата замещенной функции илиcovariant классам функций. Если функцияD​::​f переопределяет функциюB​::​f, возвращаемые типы функций являются ковариантными, если они удовлетворяют следующим критериям:

  • оба являются указателями на классы, оба являются ссылками lvalue на классы или оба являются ссылками rvalue на классы112

  • класс в возвращаемом типеB​::​f является тем же классом, что и класс в возвращаемом типеD​::​f, или является однозначным и доступным прямым или косвенным базовым классом класса в возвращаемом типеD​::​f

  • оба указателя или ссылки имеют одинаковую квалификацию cv, а тип класса в типе возвращаемого значенияD​::​f имеет такую ​​же квалификацию CV или меньшую квалификацию, чем тип класса в типе возвращаемого значенияB​::​f.

Если тип класса в ковариантном возвращаемом типеD​::​f отличается от типа B​::​fкласса, тип класса в возвращаемом типеD​::​f должен быть полным в момент объявленияD​::​f или должен быть типом классаD. Когда переопределяющая функция вызывается как конечный переопределитель переопределенной функции, ее результат преобразуется в тип, возвращаемый (статически выбранной) переопределенной функцией ([expr.call]). [Example:

class B { };
class D : private B { friend class Derived; };
struct Base {
  virtual void vf1();
  virtual void vf2();
  virtual void vf3();
  virtual B*   vf4();
  virtual B*   vf5();
  void f();
};

struct No_good : public Base {
  D*  vf4();        // error: B (base class of D) inaccessible
};

class A;
struct Derived : public Base {
    void vf1();     // virtual and overrides Base​::​vf1()
    void vf2(int);  // not virtual, hides Base​::​vf2()
    char vf3();     // error: invalid difference in return type only
    D*   vf4();     // OK: returns pointer to derived class
    A*   vf5();     // error: returns pointer to incomplete class
    void f();
};

void g() {
  Derived d;
  Base* bp = &d;                // standard conversion:
                                // Derived* to Base*
  bp->vf1();                    // calls Derived​::​vf1()
  bp->vf2();                    // calls Base​::​vf2()
  bp->f();                      // calls Base​::​f() (not virtual)
  B*  p = bp->vf4();            // calls Derived​::​pf() and converts the
                                // result to B*
  Derived*  dp = &d;
  D*  q = dp->vf4();            // calls Derived​::​pf() and does not
                                // convert the result to B*
  dp->vf2();                    // ill-formed: argument mismatch
}

end example]

[ Note: Интерпретация вызова виртуальной функции зависит от типа объекта, для которого она вызывается (динамический тип), тогда как интерпретация вызова невиртуальной функции-члена зависит только от типа указателя или ссылка, обозначающая этот объект (статический тип) ([expr.call]). ]end note

[ Note: Спецификаторvirtual подразумевает членство, поэтому виртуальная функция не может быть функцией, не являющейся членом ([dcl.fct.spec]). Виртуальная функция также не может быть статическим членом, поскольку вызов виртуальной функции полагается на конкретный объект для определения того, какую функцию вызывать. Виртуальная функция, объявленная в одном классе, может быть объявленаfriend в другом классе. ]end note

Виртуальная функция, объявленная в классе, должна быть определена или объявлена pure в этом классе, либо в том и другом; диагностика не требуется ([basic.def.odr]).

[ Example: Вот некоторые варианты использования виртуальных функций с несколькими базовыми классами:

struct A {
  virtual void f();
};

struct B1 : A {                 // note non-virtual derivation
  void f();
};

struct B2 : A {
  void f();
};

struct D : B1, B2 {             // D has two separate A subobjects
};

void foo() {
  D   d;
//   A*  ap = &d;                  // would be ill-formed: ambiguous
  B1*  b1p = &d;
  A*   ap = b1p;
  D*   dp = &d;
  ap->f();                      // calls D​::​B1​::​f
  dp->f();                      // ill-formed: ambiguous
}

В классеD выше есть два вхождения классаA и, следовательно, два вхождения виртуальной функции-членаA​::​f. Последний приоритетB1​::​A​::​f - этоB1​::​f и последний приоритетB2​::​A​::​f - этоB2​::​f. ]end example

[ Example: В следующем примере показана функция, не имеющая уникального окончательного переопределителя:

struct A {
  virtual void f();
};

struct VB1 : virtual A {        // note virtual derivation
  void f();
};

struct VB2 : virtual A {
  void f();
};

struct Error : VB1, VB2 {       // ill-formed
};

struct Okay : VB1, VB2 {
  void f();
};

ОбаVB1​::​f иVB2​::​f переопределяют,A​::​f но в классе нет их переопределенияError. Следовательно, этот пример неверен. Однако классOkay хорошо сформирован, потому чтоOkay​::​f он окончательный приоритет. ]end example

[ Example: В следующем примере используются хорошо сформированные классы, указанные выше.

struct VB1a : virtual A {       // does not declare f
};

struct Da : VB1a, VB2 {
};

void foe() {
  VB1a*  vb1ap = new Da;
  vb1ap->f();                   // calls VB2​::​f
}

end example]

Явная квалификация с помощью оператора области видимости ([expr.prim]) подавляет механизм виртуального вызова. [Example:

class B { public: virtual void f(); };
class D : public B { public: void f(); };

void D::f() { /* ... */ B::f(); }

Здесь вызов функции D​::​f действительно вызывает,B​::​f а не вызывает D​::​f. ]end example

Функция с удаленным определением ([dcl.fct.def]) не должна переопределять функцию, у которой нет удаленного определения. Точно так же функция, у которой нет удаленного определения, не должна переопределять функцию с удаленным определением.

Функция с тем же именем, но с другим списком параметров (Clause[over]) в качестве виртуальной функции не обязательно является виртуальной и не отменяет. Использованиеvirtual спецификатора в объявлении замещающей функции допустимо, но избыточно (имеет пустую семантику).Access control не учитывается при определении приоритета.

Многоуровневые указатели на классы или ссылки на многоуровневые указатели на классы не допускаются.