【C++】基底クラス型のポインタの使い方を解説!

C++で基底クラス型のポインタ基底クラスへのポインタと呼ばれるものがあります。今回は、

  • 基底クラス型のポインタって何?
  • どんなメリットがあるの?

という方に向けて、基底クラス型のポインタについてわかりやすく解説していきます。

無料体験ができるプログラミングスクールの詳細記事

 

基底クラス型のポインタ

基底クラス型のポインタ基底クラスへのポインタは同じ意味で、継承元クラス型で定義したポインタという意味です。(以下基底クラス型のポインタとします)わかりやすいように基底クラスと派生クラスからお話ししましょう。

基底クラスと派生クラスとは(継承関係)

基底クラスと派生クラスをもうちょっとわかりやすく言えば、親クラスと子クラスです。つまりは継承の話です。いまは純粋仮想関数は気にしないでください。

#include <iostream>
using namespace std;

//人間のクラス(基底クラス)
class Human {
public:
	//純粋仮想関数
	void virtual Hello() = 0;
};

//アメリカ人のクラス(派生クラス)
class American : public Human {
public:
	//挨拶のメンバ関数
	void Hello() { cout << "Hello" << endl; }
};

//日本人のクラス(派生クラス)
class Japanese : public Human {
public:
	//挨拶のメンバ関数
	void Hello() { cout << "こんにちは" << endl; }
};

Humanが基底(親)クラスで、AmericanとJapaneseが派生(子)クラスです。

基底クラス Human

派生クラス American

派生クラス Japanese

わからない方はこちらの記事を読んでいただければと思います。

【C++】継承とは?わかりやすく解説!

基底クラス型のポインタとは

基底クラス型のポインタを実際に作ってみます。比べるために派生クラス型のポインタも作ってみます。

class BaseClass {
public:
	int value;
	//コンストラクタ
	BaseClass(int _value) { value = _value; }
};

//派生クラス定義
class DerivedClass : public BaseClass {
public:
	//コンストラクタ
	DerivedClass(int _value) : BaseClass(_value) {}
};


int main() {

	//基底クラス型のポインタ作成
	BaseClass* BaseClassPointer;

	//派生クラス型のポインタ作成
	DerivedClass* DerivedClassPointer;

}

baseClassPointer基底クラス型のポインタです。違う名前でもかまわないです。

ただ基底クラス型ポインタを宣言しているだけですが作り方はこうです。

実際に基底クラスのポインタを入れて操作することができます。

	//基底クラスへのポインタ作成
	BaseClass* BaseClassPointer;

	//基底クラスオブジェクト作成
	BaseClass B(1);

	//基底クラスポインタ代入
	BaseClassPointer = &B;
	BaseClassPointer->value = 5;

次に話すことなのですが、派生クラスも入れることができます。

	//基底クラスへのポインタ作成
	BaseClass* BaseClassPointer;

	//派生クラスオブジェクト作成
	DerivedClass D(1);

	//派生クラスポインタ代入
	BaseClassPointer = &D;
	BaseClassPointer->value = 5;

基底クラス型のポインタのメリット

まず基底クラス型のポインタのメリットを知るには、派生クラス型のポインタができないことを知る必要があります。

派生クラス型のポインタができないこと

このソースコードとイメージ図を照らし合わせていただければわかると思います。

//基底クラス
class BaseClass {
public:
	int value;
	//コンストラクタ
	BaseClass(int _value) { value = _value; }
};

//派生クラス
class DerivedClass : public BaseClass {
public:
	//コンストラクタ
	DerivedClass(int _value) : BaseClass(_value) {}
};

int main() {

	//基底型と派生型オブジェクト作成
	BaseClass B(1);
	DerivedClass D(1);


	//基底クラス型のポインタ作成
	BaseClass* BaseClassPointer;

	//どちらも代入できる
	BaseClassPointer = &B; //OK
	BaseClassPointer = &D; //OK


	//派生クラス型のポインタ作成
	DerivedClass* DerivedClassPointer;

	//基底クラスのポインタは代入不可
	DerivedClassPointer = &B; //NG
	DerivedClassPointer = &D; //OK

}

イメージはこんな感じ。基底クラスのほうが強いイメージ。小さい箱には大きいものが入らないというイメージです。

  • 基底の箱 = 基底
  • 基底の箱 > 派生
  • 派生の箱 < 基底 ×NG
  • 派生の箱 = 派生

基底(親)クラス

親は子すべてを代入できます。

派生(子)クラス

子は親を代入できません。

派生クラス型のポインタは基底クラスのポインタを入れることはできません。言い換えれば、

基底クラス型のポインタは、派生クラスのポインタも代入することができる

ということです。ちなみにこれがすごく重要です。

この基底クラス型のポインタを利用するとこんなことができます。

#include <iostream>
using namespace std;

//基底クラス
class BaseClass {
public:
	int value;
	//コンストラクタ
	BaseClass(int _value) { value = _value; }
};

//派生クラス
class DerivedClass : public BaseClass {
public:
	//コンストラクタ
	DerivedClass(int _value) : BaseClass(_value) {}
};


int main() {

	//基底型と派生型オブジェクト作成
	BaseClass B(1);
	DerivedClass D(2);

	//基底クラス型のポインタの配列pを作成
	BaseClass* BaseClassPointer[] = { &B,&D,&B,&B,&D };

	//for文で回す
	for (int i = 0; i < 5; i++) {
		cout << BaseClassPointer[i]->value << endl;
	}
}

BaseClassPointer[]というのが基底クラス型ポインタの配列になっていて、中には&B(基底クラスポインタ)や&D(派生クラスポインタ)が混在しています。

そのあとにfor文で基底クラスオブジェクトと派生クラスオブジェクトのvalueの値を表示しています。実行結果はこのように出ます。

1
2
1
1
2

このような使い方ができるのが大きなメリットです。

ポリモーフィズムの使用例

この基底クラス型のポインタのメリットを生かしたものにポリモーフィズムというものがあります。

興味のある方のためにポリモーフィズムを使ったサンプルコードを書いておきます。

#include <iostream>
#define N 7
using namespace std;
 
//すべての親となる基底クラス
class Human {
public:
 void virtual Hello() = 0;
};
 
//アメリカ人のクラス
class American : public Human {
public:
 //挨拶のメンバ関数
 void Hello() { cout << "Hello" << endl; }
};
 
//日本人のクラス
class Japanese : public Human {
public:
//挨拶のメンバ関数
 void Hello() { cout << "こんにちは" << endl; }
};
 
//インド人のクラス
class Indian : public Human {
public:
 //挨拶のメンバ関数
 void Hello() { cout << "ナマステ" << endl; }
};
 
//韓国人のクラス
class Korean : public Human {
public:
 //挨拶のメンバ関数
 void Hello() { cout << "アニョハセヨ" << endl; }
};
 
//中国人のクラス
class Chinese : public Human {
public:
 //挨拶のメンバ関数
 void Hello() { cout << "ニーハオ" << endl; }
};
 
//ロシア人のクラス
class Russian : public Human {
public:
 //挨拶のメンバ関数
 void Hello() { cout << "ドーブライ ディエン" << endl; }
};
 
//ドイツ人のクラス
class German : public Human {
public:
 //挨拶のメンバ関数
 void Hello() { cout << "グーテン ターク" << endl; }
};
 
int main()
{
//それぞれのクラスのオブジェクトを作成
 American american;
 Japanese japanese;
 Indian indian;
 Korean korean;
 Chinese chinese;
 Russian russian;
 German german;
 
//それぞれのオブジェクトのポインタを格納する配列を用意
 Human* human[N] = 
 { &american,&japanese,&indian,&korean,&chinese,&russian,&german };
 
//それぞれでHello()関数を呼び出す 
 for (int i = 0; i < N; i++) {
 human[i]->Hello();
 }
}

こちらで解説しています。

ポリモーフィズムとはなにか?超わかりやすく解説します!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です