18.3 The override and final specifiers, and covariant return types


https://www.learncpp.com/cpp-tutorial/the-override-and-final-specifiers-and-covariant-return-types/
継承でよく見られる問題を処理するには、2つの特殊な識別子があります.
書き換えとfinal
ちなみにこの2つのidentifierはkeywordではありません
finalはあまり使われませんが、overrideは適切に使用するときに多くの助けを提供します.
このレッスンでは、2つの識別子だけでなく、virtualfunctionがreturn typeマッチングを要求する反例も表示します.

The override specifier


前のレッスンで説明したように、派生クラスのvirtual functionは、署名とreturn typeが一致したときに上書きと見なされる必要があります.
これは不注意な問題を引き起こす.上書きしようとしたが、上書きされなかった場合
例を見てみましょう
#include <iostream>
#include <string_view>

class A
{
public:
	virtual std::string_view getName1(int x) { return "A"; }
	virtual std::string_view getName2(int x) { return "A"; }
};

class B : public A
{
public:
	virtual std::string_view getName1(short int x) { return "B"; } // note: parameter is a short int
	virtual std::string_view getName2(int x) const { return "B"; } // note: function is const
};

int main()
{
	B b{};
	A& rBase{ b };
	std::cout << rBase.getName1(1) << '\n';
	std::cout << rBase.getName2(2) << '\n';

	return 0;
}
rBaseはAへの参照です.でもgetName 1,2はvirtualfunction
ただし、BクラスではgetName 1はパラメータが異なる形式である.異なる
getName 2もconstとして宣言され、異なる署名があります.
どちらも書き換えと見なされない
従って、出力は以下のようになる
A
A
return typeが同じで名前が同じでパラメータが異なる場合に注意してください
関数のリロードとみなされます.
上記の場合、意図しない書き換えと考えられる
より複雑なプログラムでこれらの問題をデバッグするのはもっと難しいかもしれません.
これらの問題を解決するには、override識別子をconst位置に配置します.
funtcionがbaseclassのfunctionを上書きしていない場合、errorが与えられます.
#include <string_view>

class A
{
public:
	virtual std::string_view getName1(int x) { return "A"; }
	virtual std::string_view getName2(int x) { return "A"; }
	virtual std::string_view getName3(int x) { return "A"; }
};

class B : public A
{
public:
	std::string_view getName1(short int x) override { return "B"; } // compile error, function is not an override
	std::string_view getName2(int x) const override { return "B"; } // compile error, function is not an override
	std::string_view getName3(int x) override { return "B"; } // okay, function is an override of A::getName3(int)

};

int main()
{
	return 0;
}
上のプログラムでは2つのコンパイルエラーが発生します
getName 1とgetName 2が正しく書き換えられていないため
override identifierは、プログラマーがパフォーマンスに影響を及ぼさないように支援します.
追加のoverride識別子はvirtualの意味を含むため、virtualを省略することができる

The final specifier


クラスの一部の関数は、継承または上書きされたくない場合があります.
final説明子はコンパイラに強制的に実行できます
final in関数を上書きまたは継承しようとするとcompile errorが発生します
final説明子もoverrideと同じ位置にあります
#include <string_view>

class A
{
public:
	virtual std::string_view getName() { return "A"; }
};

class B : public A
{
public:
	// note use of final specifier on following line -- that makes this function no longer overridable
	std::string_view getName() override final { return "B"; } // okay, overrides A::getName()
};

class C : public B
{
public:
	std::string_view getName() override { return "C"; } // compile error: overrides B::getName(), which is final
};
上のgetName()はfinal関数で、Cが上書きしようとするとコンパイルエラーが発生します.
クラス継承自体を防止するには、クラス名の後にfinalを付けることができます.
#include <string_view>

class A
{
public:
	virtual std::string_view getName() { return "A"; }
};

class B final : public A // note use of final specifier here
{
public:
	std::string_view getName() override { return "B"; }
};

class C : public B // compile error: cannot inherit from final class
{
public:
	std::string_view getName() override { return "C"; }
};
class B finalとすると、class CはBを公的に継承できない

Covariant return types


派生クラスのvirtual functionには、異なる戻りタイプの特殊な状況がある場合があります.このときoverrideは変わらない.
virtualfunctionのreturn typeがBaseクラスのpointerまたはreferenceである場合、override関数は派生クラスのpointerまたはreferenceに変更できます.
このときreturn typeをコヒーレントreturn typeと呼ぶ
例を見てみましょう
#include <iostream>
#include <string_view>

class Base
{
public:
	// This version of getThis() returns a pointer to a Base class
	virtual Base* getThis() { std::cout << "called Base::getThis()\n"; return this; }
	void printType() { std::cout << "returned a Base\n"; }
};

class Derived : public Base
{
public:
	// Normally override functions have to return objects of the same type as the base function
	// However, because Derived is derived from Base, it's okay to return Derived* instead of Base*
	Derived* getThis() override { std::cout << "called Derived::getThis()\n";  return this; }
	void printType() { std::cout << "returned a Derived\n"; }
};

int main()
{
	Derived d{};
	Base* b{ &d };
	d.getThis()->printType(); // calls Derived::getThis(), returns a Derived*, calls Derived::printType
	b->getThis()->printType(); // calls Derived::getThis(), returns a Base*, calls Base::printType

	return 0;
}
getThisはVirtual,override,return typeでBase pointerとDerived pointerとは異なる
しかし、ここでは上書きの問題は発生しません.
上記のプログラムを実行し、次のように出力します.
called Derived::getThis()
returned a Derived
called Derived::getThis()
returned a Base
ここで注目すべきケースはbの場合です
bがgetThisを呼び出すとvirtualのためにDerived::getThis()が呼び出されます.
そしてここでthisを返します.このときthisはBaseタイプです.
したがって、Derivedは戻り値ですが、upacastingでBase pointerを返します.
したがって、printtypeはBaseのprintType()を呼び出します.