MyTetra Share
Делитесь знаниями!
Кратко: что делают модификаторы override и final
Время создания: 20.08.2021 11:37
Автор: Xintrea
Текстовые метки: c++, c++11, Си++, модификатор, override, final
Раздел: Компьютер - Программирование - Язык C++ (Си++)
Запись: xintrea/mytetra_syncro/master/base/1629448631tneki83k8s/text.html на raw.github.com

Модификатор override


Модификатор override появился в стандарте C++11 и используется при описании методов дочернего класса.


Модификатор override следует писать для тех методов, которые по задумке программиста являются переопределенными методами базового класса. Данный модификатор пишется после имени метода. В заголовочном файле описание метода с использованием override выглядит так:



class B : public A

{

public:

virtual const char* getVersion() override;

};



В реализации (т. е. в *.cpp-файле), модификатор override не пишется.


Модификатор override позволяет компилятору следить за тем, чтобы метод, помеченный этим модификатором действительно переопределял метод базового класса. В языке C++ метод дочернего класса будет переопределять метод базового класса только в том случае, если его сигнатура полностью совпадает с сигнатурой базового класса (одно исключение из этого правила есть, оно написано ниже). В больших проектах, или просто по запарке, можно не обратить внимание, например, на то, что в одной сигнатуре используется обычный указатель, а в другой константный указатель. И программист будет думать что он переопределил метод, а на самом деле переопределения не произошло. Чтобы избежать таких ошибок, рекомендуется для каждого метода, который переопределяет базовый метод, писать модификатор override.



Модификатор override и ковариантный тип возврата


Есть один случай, когда тип возврата переопределения может не совпадать с типом возврата виртуального метода родительского класса, но при этом оставаться переопределением:


Если базовый виртуальный метод возвращает указатель или ссылку на класс, то переопределенные методы могут возвращать указатель или ссылку на свой собственный класс. Другими словами, в переопределенных методах в качестве возвращаемого типа можно вместо указателя на родительский класс возвращать указатель на дочерний класс. Это называется ковариантным типом возврата.


Например:


#include <iostream>


class Parent

{

public:


// Этот метод getThis() возвращает указатель на класс Parent

virtual Parent* getThis()

{

std::cout << "called Parent::getThis()\n";

return this;

}

void printType()

{

std::cout << "returned a Parent\n";

}

};


class Child : public Parent

{

public:

// Обычно, типы возврата переопределений и виртуальных функций

// родительского класса должны совпадать.

// Однако, поскольку Child наследует класс Parent, следующий метод

// может возвращать Child* вместо Parent*

virtual Child* getThis()

{

std::cout << "called Child::getThis()\n";

return this;

}


void printType()

{

std::cout << "returned a Child\n";

}

};


int main()

{

Child ch;

Parent *p = &ch;


// Вызывается Child::getThis(), возвращается Child*, вызывается Child::printType

ch.getThis()->printType();


// Вызывается Child::getThis(), возвращается Parent*, вызывается Parent::printType

p->getThis()->printType();

}



Результат выполнения программы:



called Child::getThis()
returned a Child
called Child::getThis()
returned a Parent



Внимание! Некоторые старые компиляторы могут не поддерживать ковариантные типы возврата.


В примере, приведенном выше, сначала вызывается ch.getThis(). Поскольку ch является объектом класса Child, то вызывается Child::getThis(), который возвращает Child*. Этот Child* затем используется для вызова невиртуальной функции Child::printType().


Затем выполняется p->getThis(). Переменная p является указателем класса Parent на объект ch класса Child. Parent::getThis() — это виртуальная функция, поэтому вызывается переопределение Child::getThis(). Хотя Child::getThis() и возвращает Child*, но, поскольку родительская часть объекта возвращает Parent*, возвращаемый Child* преобразовывается в Parent*. И, таким образом, вызывается Parent::printType().


Тип Child* в данном примере будет возвращен только в том случае, если будем вызывать метод getThis() с объектом класса Child.



Модификатор final


Модификатор final используется для двух случаев:


  • чтобы пометить метод базового класса, который не должен переопределяться;
  • чтобы пометитить весь класс что он не должен наследоваться.


Если пользователь пытается переопределить метод или наследовать класс с модификатором final, то компилятор выдаст ошибку.


Модификатор final, так же как модификатор override, пишется после имени метода или имени класса.


Описание неизменяемого метода:


virtual const char* getVersion() override final;


Описание неизменяемого класса:


class B final : public A

{

public:

virtual const char* getVersion() override;

};


При сборке программы компилятор проверяет, не происходит ли попытки переопределить "финализированный" метод или класс, и если находит, то генерирует ошибку времени компиляции.


Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования