1. ホーム

C++におけるポリモーフィズム

2022-03-07 08:51:16
<パス

1. ポリモーフィズムの初見

オブジェクト指向において、異なるオブジェクトが同じメッセージを受け取り、異なる振る舞いをすることをポリモーフィズムという。C++プログラミングでは ポリモーフィズムとは、異なるが類似した処理を行う異なる関数を一つの名前で定義し、異なる内容を持つ関数を同じ関数名で呼び出せるようにすることである . つまり、同じインターフェイスを使って、異なる機能を持つ関数にアクセスすることができ、「1つのインターフェイスに多くのメソッド」を実現することができるのです。

実際、プログラミングではポリモーフィズムがよく使われる。最も簡単な例は演算子である。例えば、整数型、浮動小数点数型、倍精度型の足し算は、実はそれぞれ異なる内容を持つ関数で実装されているのですが、+演算子を使って足し算をすることができます。この例では、多相性機能を利用している。

C++では、ポリモーフィズムの実装は、以下のようになります。 連想コード化(バインディングともいう) この考え方は関連しています。ソースプログラムをコンパイルしてリンクし、実行ファイルにするプロセスは、実行可能なコードを一緒に構築(またはアセンブル)するプロセスである。として、実行前にビルドを行うところ。 静的ビルド(プリビルド) と呼ばれ、実行時に行われるビルドは ダイナミックビルド(レイトビルド) .

静的ビルドでサポートされるポリモーフィズムは、以下のように呼ばれます。 コンパイル時ポリモーフィズム(静的ポリモーフィズム) . C++では、コンパイル時多相性は、関数のオーバーローディングとテンプレートによって実現されます。関数のオーバーロードの仕組みを使うと、同じ名前の関数を呼び出すときに、コンパイルシステムは実引数の仕様に基づいてどの関数が呼び出されるかを判断する。

ダイナミックビルドでサポートされるポリモーフィズムは、以下のように呼ばれます。 ランタイムポリモーフィズム(動的ポリモーフィズム) . C++では、実行時ポリモーフィズムは仮想関数で実装されています。

もうひとつわかりやすい例を挙げると、たとえばチケットを買うという行為は、普通の人は定価、学生は半額といった具合です。

2. ポリモーフィズムの定義と実装

2.1 ポリモーフィック定義構成条件

ポリモーフィズムとは、異なる継承関係を持つクラスのオブジェクトが、同じ関数を呼び出そうとすると、異なる振る舞いをすることです。例えば、StudentはPersonを継承する。Personは正規の値段でチケットを購入し、Studentは半額でチケットを購入する。

次に、継承におけるポリモーフィズムを構成するために必要な条件があと2つある。
a. 関数を呼び出すオブジェクトは、必ず ポインタ または リファレンス .
b. 呼び出される関数は、以下のものでなければならない。 仮想関数 で、仮想関数のオーバーライドを完了させる。

仮想関数とは何ですか?
ダミー関数:クラスのメンバ関数で、先頭に 仮想 というキーワードがあります。

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "BuyTicket-FullPrice" << endl;
	}
};


仮想関数のオーバーライドとは何ですか?
仮想関数をオーバーライドする 派生クラスがベースクラスと同一の仮想関数を持つ場合、そのサブクラスの仮想関数がベースクラスの仮想関数をオーバーライドすると言います。 "同一"とは 関数名、引数、戻り値が同じであること。 . また、仮想関数をオーバーライドすることをオーバーライドといいます。

コード例です。

#include 

#include 
Irregular rewriting behavior
A member function overridden in a derived class can be overridden without the virtual keyword, which also constitutes overriding, because the virtual function of the base class is inherited, and the properties of the virtual function remain in the derived class, we just override it. This is very irregular, and you should try not to use it this way in general.
Note: If a function in a subclass has a virtual modifier that is not present in the parent class, it constitutes a function hiding.
A destructor in a base class that is virtual overrides the destructor of the base class if the destructor of the derived class is a virtual function. Here they have different function names, which seems to violate the rules of overriding, but it is not. Here it can be understood that the compiler does something special with the names of the destructors, and the names of the destructors are uniformly treated as destructor after compilation, which also shows that
The destructor of the base class is best written as a virtual function
.
Here's a link that specifically explains why it's better to write the base class's destructor as a dummy function.
https://blog.csdn.net/komtao520/article/details/82424468
Interface inheritance vs. implementation inheritance

The inheritance of ordinary functions is a kind of implementation inheritance, the derived class inherits the base class function, can use the function, inherited is the implementation of the function. The inheritance of virtual functions is a kind of interface inheritance, where the derived class inherits the interface of the base class virtual function for the purpose of rewriting it, reaching polymorphism, and inheriting the interface. So don't define a function as a virtual function if you don't implement polymorphism. 3. abstract class If you write = 0 after a virtual function, the function is purely virtual. A class that contains a pure virtual function is called an abstract class (also called an interface class), and an abstract class cannot instantiate an object. Derived classes also cannot instantiate objects after inheritance . Only by overriding pure virtual functions can a derived class instantiate an object. The pure virtual function specifies that the derived class must be overridden, and the pure virtual function is an example of interface inheritance. Example code.
#include

#include 
Result: Benz-comfort

BMW-Handling 4. The principle of polymorphism 4.1 The virtual function table // Calculate sizeof(b) as what? #include #include
By testing we find that sizeof(Base) is 8 bytes in size. In addition to the _b member, there is an additional _vfptr placed in front of the object (note that some platforms may place it after the object, this is platform dependent), and this pointer in the object we call it a virtual function table pointer . A class containing a virtual function has at least one virtual function table pointer, because the address of the virtual function is placed in the virtual function table ( virtual table ).
Example code. //1. Add a derived class Derive to inherit from Base //2. Rewrite Func1 in Derive //Base then adds a dummy function Fun2 and a normal function Fun3 class Base { virtual void Func1() { cout << "Base::Func1()" << endl; } virtual void Func2(){ cout << "Base::Func2()" << endl; } void Func3(){ cout << "Base::Func3()" << endl; } private: int _b = 1; }; class Derive : public Base { public: virtual void Func1(){ cout << "Derive::Func1()" << endl; } private: int _d = 2; }; int main() { Base b; Derive d; system("pause"); return 0; } Here we open the monitoring window and it is easy to see the following points.


a. Derived class objects also have a pointer to a virtual table. d objects consist of two parts, one for members inherited from the parent class, and the other for its own members.
b. The virtual table of the base class b object and the derived class d object are not the same, and here we find Func1 is overwritten, so d's virtual table holds the overwritten Derive::Func1 so the override of the virtual function is also called an override.
c. Also Func2 is inherited and is a virtual function, so it is put in the virtual table, and Func3 is inherited, but is not a virtual function, so it is not put in the virtual table.
d. The virtual function table is essentially an array of pointers that holds pointers to virtual functions, and this array has a nullptr placed at the end.
To summarize: virtual table generation for derived classes.
(1) First copy the contents of the virtual table of the base class to the virtual table of the derived class.
(2) If the derived class overrides a virtual function in the base class, overwrite the virtual function of the base class in the virtual table with the derived class's own virtual function.
(3) The newly added virtual functions of the derived class itself are added to the end of the derived class's virtual table in the order they are declared in the derived class. Principle of polymorphic implementation.
After all this analysis, let's take the above "BuyTicket" example, the Func function is called Person::BuyTicket for Person and Student::BuyTicket for Student. class Person { public: virtual void BuyTicket() { cout << "BuyTicket-FullPrice" << endl; } }; class Student : public Person { public: virtual void BuyTicket(){ cout << "BuyTickets-HalfPrice" << endl; } }; void Func(Person& p) { p.BuyTicket(); } int main() { Person Mike; Func(Mike); Student John; Func(John); system("pause"); return 0; }
This achieves different objects to show different forms when going through the same behavior. Dynamic vs. static bindings.
a. Static binding is also known as pre-binding (early binding). determines the behavior of the program during compilation, also known as static polymorphism e.g., function overloading.
b. Dynamic binding, also known as late binding (late binding), determines the specific behavior of a program during program runtime, calling specific functions based on the type of specific get Also known as dynamic polymorphism . A link to this blog post that explains polymorphism better is posted below for your benefit.
https://blog.csdn.net/u012630961/article/details/81226351
#include #include Result: Benz-comfort
BMW-Handling 4. The principle of polymorphism 4.1 The virtual function table // Calculate sizeof(b) as what? #include #include
By testing we find that sizeof(Base) is 8 bytes in size. In addition to the _b member, there is an additional _vfptr placed in front of the object (note that some platforms may place it after the object, this is platform dependent), and this pointer in the object we call it a virtual function table pointer . A class containing a virtual function has at least one virtual function table pointer, because the address of the virtual function is placed in the virtual function table ( virtual table ).
Example code. //1. Add a derived class Derive to inherit from Base //2. Rewrite Func1 in Derive //Base then adds a dummy function Fun2 and a normal function Fun3 class Base { virtual void Func1() { cout << "Base::Func1()" << endl; } virtual void Func2(){ cout << "Base::Func2()" << endl; } void Func3(){ cout << "Base::Func3()" << endl; } private: int _b = 1; }; class Derive : public Base { public: virtual void Func1(){ cout << "Derive::Func1()" << endl; } private: int _d = 2; }; int main() { Base b; Derive d; system("pause"); return 0; } Here we open the monitoring window and it is easy to see the following points.


a. Derived class objects also have a pointer to a virtual table. d objects consist of two parts, one for members inherited from the parent class, and the other for its own members.
b. The virtual table of the base class b object and the derived class d object are not the same, and here we find Func1 is overwritten, so d's virtual table holds the overwritten Derive::Func1 so the override of the virtual function is also called an override.
c. Also Func2 is inherited and is a virtual function, so it is put in the virtual table, and Func3 is inherited, but is not a virtual function, so it is not put in the virtual table.
d. The virtual function table is essentially an array of pointers that holds pointers to virtual functions, and this array has a nullptr placed at the end.
To summarize: virtual table generation for derived classes.
(1) First copy the contents of the virtual table of the base class to the virtual table of the derived class.
(2) If the derived class overrides a virtual function in the base class, overwrite the virtual function of the base class in the virtual table with the derived class's own virtual function.
(3) The newly added virtual functions of the derived class itself are added to the end of the derived class's virtual table in the order they are declared in the derived class. Principle of polymorphic implementation.
After all this analysis, let's take the above "BuyTicket" example, the Func function is called Person::BuyTicket for Person and Student::BuyTicket for Student. class Person { public: virtual void BuyTicket() { cout << "BuyTicket-FullPrice" << endl; } }; class Student : public Person { public: virtual void BuyTicket(){ cout << "BuyTickets-HalfPrice" << endl; } }; void Func(Person& p) { p.BuyTicket(); } int main() { Person Mike; Func(Mike); Student John; Func(John); system("pause"); return 0; }
This achieves different objects to show different forms when going through the same behavior. Dynamic vs. static bindings.
a. Static binding is also known as pre-binding (early binding). determines the behavior of the program during compilation, also known as static polymorphism e.g., function overloading.
b. Dynamic binding, also known as late binding (late binding), determines the specific behavior of a program during program runtime, calling specific functions based on the type of specific get Also known as dynamic polymorphism . A link to this blog post that explains polymorphism better is posted below for your benefit.
https://blog.csdn.net/u012630961/article/details/81226351