仮想関数とオーバーライド
C++では、基底クラスのメソッドを派生クラスで再定義することをオーバーライドと呼びます。オーバーライドは、基底クラスのメソッドが仮想関数である場合にのみ可能です。
仮想関数とは
仮想関数は、基底クラスで宣言され、派生クラスでオーバーライドすることができる関数です。仮想関数は virtual
キーワードを使用して宣言されます。
class Base {
public:
virtual void func() {
std::cout << "Base::func()" << std::endl;
}
};
オーバーライドとは
オーバーライドは、派生クラスが基底クラスの仮想関数を再定義することです。オーバーライドされた関数は、基底クラスの関数を隠し、その関数の新しい実装を提供します。
class Derived : public Base {
public:
void func() override { // C++11以降では、overrideキーワードを使用して明示的にオーバーライドを示すことが推奨されています。
std::cout << "Derived::func()" << std::endl;
}
};
以上が、C++の仮想関数とオーバーライドの基本的な説明です。次のセクションでは、具体的なオーバーライドできない状況とその理由について詳しく説明します。
オーバーライドできない状況
C++では、以下のような状況でオーバーライドができない、またはオーバーライドが期待通りに機能しないことがあります。
非仮想関数のオーバーライド
C++では、非仮想関数はオーバーライドできません。基底クラスの非仮想関数を派生クラスで再定義すると、それは新しい関数として扱われます。
class Base {
public:
void func() {
std::cout << "Base::func()" << std::endl;
}
};
class Derived : public Base {
public:
void func() { // これは新しい関数として扱われます。
std::cout << "Derived::func()" << std::endl;
}
};
シグネチャの不一致
関数のシグネチャ(関数名、引数の型と数、const修飾子)が基底クラスの仮想関数と一致しない場合、オーバーライドはできません。
class Base {
public:
virtual void func(int x) {
std::cout << "Base::func()" << std::endl;
}
};
class Derived : public Base {
public:
void func(double x) { // 引数の型が異なるため、オーバーライドではなく新しい関数として扱われます。
std::cout << "Derived::func()" << std::endl;
}
};
以上が、C++でオーバーライドできない状況の一部です。次のセクションでは、C++11のoverride
とfinal
キーワードについて詳しく説明します。
C++11のoverrideとfinalキーワード
C++11では、override
とfinal
という2つの新しいキーワードが導入されました。これらのキーワードは、オーバーライドの挙動をより明確にし、コードの可読性と安全性を向上させます。
overrideキーワード
override
キーワードは、派生クラスの関数が基底クラスの仮想関数をオーバーライドすることを明示的に示します。これにより、コンパイラはオーバーライドの正しさをチェックし、間違いを防ぐことができます。
class Base {
public:
virtual void func() {
std::cout << "Base::func()" << std::endl;
}
};
class Derived : public Base {
public:
void func() override { // overrideキーワードを使用してオーバーライドを明示
std::cout << "Derived::func()" << std::endl;
}
};
finalキーワード
final
キーワードは、派生クラスが基底クラスの仮想関数をこれ以上オーバーライドできないことを示します。また、クラス自体をfinalにすることで、そのクラスから派生することを防ぐこともできます。
class Base {
public:
virtual void func() final { // finalキーワードを使用してこれ以上のオーバーライドを禁止
std::cout << "Base::func()" << std::endl;
}
};
class FinalClass final { // finalキーワードを使用してこれ以上の派生を禁止
// ...
};
以上が、C++11のoverride
とfinal
キーワードの基本的な説明です。次のセクションでは、これらのキーワードを使用した実例とその解説を行います。
実例と解説
ここでは、C++のオーバーライドに関する具体的な実例とその解説を提供します。
overrideキーワードの使用例
以下のコードは、override
キーワードを使用したオーバーライドの例です。
class Base {
public:
virtual void func() {
std::cout << "Base::func()" << std::endl;
}
};
class Derived : public Base {
public:
void func() override { // overrideキーワードを使用
std::cout << "Derived::func()" << std::endl;
}
};
int main() {
Base* b = new Derived();
b->func(); // "Derived::func()"が出力されます
delete b;
return 0;
}
このコードでは、Derived
クラスがBase
クラスのfunc
関数をオーバーライドしています。override
キーワードを使用することで、コンパイラはfunc
関数が正しくオーバーライドされていることを確認します。
finalキーワードの使用例
以下のコードは、final
キーワードを使用したオーバーライドの禁止の例です。
class Base {
public:
virtual void func() final { // finalキーワードを使用
std::cout << "Base::func()" << std::endl;
}
};
class Derived : public Base {
public:
void func() { // エラー: Base::funcがfinalであるためオーバーライドできません
std::cout << "Derived::func()" << std::endl;
}
};
このコードでは、Base
クラスのfunc
関数がfinal
キーワードによりオーバーライドが禁止されています。そのため、Derived
クラスでfunc
関数を再定義しようとすると、コンパイラエラーが発生します。
以上が、C++のオーバーライドに関する実例とその解説です。これらの知識を活用して、C++のコードをより理解しやすく、安全に書くことができます。