C++ の仮想関数と純粋仮想関数の違い
この記事では、C++ の仮想関数と純粋仮想関数の違いについて説明します。
C++ での仮想関数とその特性
仮想関数は、ポリモーフィズムの概念と密接に関連しています。C++ では、リンクされた階層でさまざまなクラスを編成できます。これらのクラスは、一部のデータメンバーを共有し、同じメンバー関数をインターフェイスとして公開している場合があります。
一般に、コードの一部を他のクラスから継承するクラスは派生クラスと呼ばれますが、継承されるクラスは基本クラスです。これらの用語は、親子名またはスーパークラス-サブクラス名と同じ意味で使用される場合があることに注意してください。派生クラスでオーバーライドできる関数は仮想関数と呼ばれ、キーワード virtual
で宣言されます。仮想関数は、指定されたクラス階層内で同じ名前を持ち、すべての派生クラスは関数の独自の定義を実装できます。
関数がオーバーライドされていない場合、派生クラスオブジェクトは基本クラスで定義された関数を呼び出します。以下のサンプルプログラムは、Engineer
クラスと Employee
クラスの両方で print
関数を定義することにより、仮想関数の基本的な使用法を示しています。次に、Employee
への参照を受け入れ、本体内で print
関数を呼び出す任意の関数 Func
を実装できます。
現在、Func
実装は複数回変更される可能性がありますが、パラメーターとして渡されているオブジェクトに基づいて、対応する print
関数を常に呼び出します。Employee
クラス階層に複数の派生クラスを追加できます。それぞれが print
関数を実装する場合としない場合がありますが、Func
関数はそれらのインスタンスを受け入れ、正しい仮想関数を呼び出します。
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
class Employee {
public:
Employee(string fn, string ln)
: first_name(std::move(fn)), last_name(std::move(ln)) {}
virtual void print() {
cout << "name: " << first_name << "\n"
<< "last name: " << last_name << "\n";
};
protected:
string first_name, last_name;
};
class Engineer : public Employee {
public:
Engineer(string fn, string ln, string sp)
: Employee(std::move(fn), std::move(ln)), specialization(std::move(sp)) {}
void print() override {
Employee::print();
cout << "specialization: " << specialization << "\n";
}
private:
string specialization;
};
void Func(Employee &em) { em.print(); }
int main() {
Employee em1("Jim", "Jiao");
Engineer eng1("Jin", "Baker", "Aerospace Engineering");
Func(em1);
cout << "\n";
Func(eng1);
return EXIT_SUCCESS;
}
出力:
name: Jim
last name: Jiao
name: Jin
last name: Baker
specialization: Aerospace Engineering
C++ の純粋仮想関数と抽象型
一方、純粋仮想関数の概念があります。これは、通常の仮想関数と同様に宣言され、= 0;
という表記が含まれています。宣言の終わりに。これらの関数は基本的に、最初に宣言される基本クラスに定義がありません。したがって、それらは派生クラスで定義される可能性が最も高くなります。
純粋仮想関数を含むクラスは抽象クラスと呼ばれ、通常、派生クラスのインターフェースを指定するために使用されます。抽象クラスは直接インスタンス化できないことに注意してください。
次のコードスニペットは、抽象基本クラス Shape
を使用して Triangle
クラスと Rectangle
クラスを実装します。この場合、両方の派生クラスで printArea
純粋仮想関数を定義しました。派生クラスが継承された純粋仮想関数を定義せず、特定のクラス階層内の別の抽象クラスになる場合があります。派生クラスは、複数の純粋仮想関数を継承する場合があります。それらの 1つでも定義されていない場合は、抽象分類がクラスに適用されます。
#include <iostream>
#include <string>
#include <vector>
using std::cout;
using std::endl;
using std::string;
using std::vector;
class Shape {
public:
virtual void printArea() = 0;
};
class Triangle : public Shape {
public:
Triangle(double b, double h) : base(b), height(h) {}
void printArea() override { cout << (base * height) / 2.0; }
private:
double base;
double height;
};
class Rectangle : public Shape {
public:
Rectangle(double i1, double i2) : edges({i1, i2}) {}
void printArea() override { cout << edges[0] * edges[1]; }
private:
vector<double> edges;
};
int main() {
Triangle t1(3, 5);
t1.printArea();
cout << "\n";
Rectangle r1(3, 5);
r1.printArea();
cout << "\n";
return EXIT_SUCCESS;
}
出力:
7.5
15