仮想関数

■メンバ関数のオーバーライド

継承を行うと基底クラスに定義している関数に対して
派生クラスでは別の処理に変更したいと考える場合があります。
その際に基底クラスと同じ宣言内容(戻り値の型、関数名、引数)を
派生クラスに宣言し、定義することで基底クラスの関数を
派生クラスの関数に上書きすることができます。
この関数の上書きのことを関数のオーバーライドと呼びます。

●コード例
	class AnimalBase
	{
	public:
		void Nakigoe(void);
	};

	void AnimalBase::Nakigoe(void)
	{
		printf("動物の鳴き声\n");
	}

	class Cat : public AnimalBase
	{
	public:
		void Nakigoe(void);
	};

	void Cat::Nakigoe(void)
	{
		printf("ニャー\n");
	}

	class Dog : public AnimalBase
	{
	public:
		void Nakigoe(void);
	};

	void Dog::Nakigoe(void)
	{
		printf("ワン\n");
	}

	void main(void)
	{
		AnimalBase animal;
		Dog dog;
		Cat cat;

		animal.Nakigoe();
		dog.Nakigoe();
		cat.Nakigoe();
	}

	実行結果:
		動物の鳴き声
		ワン
		ニャー

	上記の結果のようにAnimalBaseのメンバ関数Nakigoeを
	Dog、Catでオーバーライドした結果
	DogのNakigoe呼び出しで「ワン」、Cat「ニャー」と表示されました。
	このようにオーバーライドを使用すると基底クラスと派生クラスで
	同じ関数名で処理の内容を変更することが可能です。

■仮想関数と多態性

●仮想関数
	仮想関数とは派生クラスでオーバーライドを行うことを
	前提とした基底クラスのメンバ関数のことです。
	メンバ関数の宣言の前に「virtual」を記述することで、
	仮想関数として認識されます。
	この仮想関数を使用することによりこの後説明する多態性が実現します。

	・書式例
		virtual 戻り値の型 関数名(引数);

	・具体例
		class AnimalBase
		{
		public:
			virtual void Nakigoe(void); // 仮想関数宣言
		};

		void AnimalBase::Nakigoe(void)
		{
			printf("動物の鳴き声\n");
		}

●純粋仮想関数
	基底クラスでは内容を定義しない仮想関数を純粋仮想関数と呼び、
	純粋仮想関数を一つでも持っているクラスのことを抽象クラスと呼びます。

	・抽象クラスの特徴
		・インタスタンス化できない
			抽象クラスは純粋仮想関数を持っているという
			性質上インスタンス化できません。
			これは定義できない関数を持っているクラスを
			インスタンス化すると間違って仮想関数を呼び出して
			エラーになることを防ぐためです。

		・継承先の仮想関数の定義の義務化
			純粋仮想関数を宣言したクラスを継承した派生クラスは
			必ず仮想関数を実装しなければならなくなり、
			実装を行わないとエラーになるのでオーバーライドを
			忘れるという心配がありません。

	・書式例
		virtual 戻り値の型 関数名(引数) = 0;

	・具体例
		class AnimalBase
		{
		public:
			// 純粋仮想関数
			virtual void Nakigoe(void) = 0;
		};

		class Cat : public AnimalBase
		{
		public:
			virtual void Nakigoe(void);
		};

		void Cat::Nakigoe(void)
		{
			printf("にゃー\n");
		}

		class Dog : public AnimalBase
		{
		};

		void main(void)
		{
			Cat cat;
			Dog dog; // Nakigoeを定義していないのでエラー
			cat.Nakigoe();
		}

		純粋仮想関数は「派生先で必ずその仮想関数を定義している」ということが
		約束されるので、基底クラスを作成する際にはよく利用されています。

●多態性
	多態性(たたいせい)とは同じ関数を呼び出しているのに		
	その関数が異なる動作を行うことをいいます。
	多態性は多様性ポリモーフィズムとも呼ばれています。
	多態性をうまく利用することでプログラミングの効率化が図れます。

	・多態性の例
		class AnimalBase
		{
		public:
			virtual void Nakigoe(void) = 0;
		};

		class Cat : public AnimalBase
		{
		public:
			virtual void Nakigoe(void);
		};

		void Cat::Nakigoe(void)
		{
			printf("ニャー\n");
		}

		class Dog : public AnimalBase
		{
		public:
			virtual void Nakigoe(void);
		};

		void Dog::Nakigoe(void)
		{
			printf("ワン\n");
		}

		void main(void)
		{
			Dog dog;
			Cat cat;

			AnimalBase *list[2] =
			{
				&dog, &cat
			};

			for (int i = 0; i < 2; i++)
			{
				list[i]->Nakigoe();
			}
		}

		実行結果:
			ワン
			ニャー

		上記のソースのようにCat、Dogに仮想関数のNakigoeを定義し、
		AnimalBaseにアップキャストをした配列listに代入します。
		その配列でNakigoeを実行した結果実行、「ワン」と「ニャー」になりました。
		このように呼び出し側が関数を一つだけ呼び出すだけで、
		キャストしているクラスによって処理結果が変化することが多態性です。