仮想関数 - (2017/12/08 (金) 16:51:22) の1つ前との変更点
追加された行は緑色になります。
削除された行は赤色になります。
*純粋仮想関数
**仮想関数
前章で virtual キーワードをつけた関数は 仮想関数 として扱われ
オーバーライドで書き換えることができると学びました。
virtual をつけずにオーバーライドした場合、オブジェクトを扱う「型」の定義に従い、
virtual をつけてオーバーライドした場合は、基底クラスの「型」として扱っても、オーバーライドされた定義が呼び出されます。
Personの自己紹介はオーバーライドされた場合派生クラスの自己紹介を優先するようになっていました。
また、オーバーライドしなくてもPersonでの定義を呼び出すことが可能です。
**純粋仮想関数
仮想関数は何かしらの定義を持ちましたが、
定義を持たない仮想関数を作ることもできます。
これを純粋仮想関数と呼び、以下のように = 0 をつけることで実現します。
virtual 戻り値型 関数名(引数) = 0;
上のように書くことで定義がないことを表しています。
オーバーライドしなければ使えません。
この純粋仮想関数のみで構成されたクラスを、抽象クラスまたはインターフェースクラスと呼びます。
インターフェースクラスは関数の定義を持っていないため、オブジェクトを作ることができません。
必ず継承し、関数を再定義して使うのです。
機能の宣言のみを持ったインターフェースですが、どのようなメリットがあるかつかみにくいと思います。
多態性の章において、基底クラスのポインタには、その派生クラスであればどんなオブジェクトでも扱うことができると書きました。
インターフェースはクラス階層の土台に位置し、抽象的な宣言のみ行います。
クラス使用者はインターフェースのみ知っていれば機能を使うことができる上
インターフェースから派生した様々なオブジェクトを同じコードで処理することができます。
*例
図形の面積を求めるサンプルです。
抽象クラスShapeを用意し
そこから派生したRectクラス、Circleクラスでそれぞれ面積の計算方法を定義します。
**Shape.h
#html2(){{
<pre class="brush: cpp;">
#pragma once
class Shape{
public:
virtual double getArea() = 0;
};
</pre>
}}
**Rect.h
#html2(){{
<pre class="brush: cpp;">
#pragma once
#include "shape.h"
class Rect : public Shape
{
private:
double width;
double height;
public:
Rect(double width, double height);
double getArea();
};
</pre>
}}
**Rect.cpp
#html2(){{
<pre class="brush: cpp;">
#include "Rect.h"
Rect::Rect(double width, double height){
this->width = width;
this->height = height;
}
double Rect::getArea(){
return this->height * this->width;
}</pre>
}}
**Circle.h
#html2(){{
<pre class="brush: cpp;">
#pragma once
#include "shape.h"
class Circle : public Shape
{
private:
double radius;
public:
Circle(double radius);
double getArea();
};
</pre>
}}
**Circle.cpp
#html2(){{
<pre class="brush: cpp;">
#include "Circle.h"
#define _USE_MATH_DEFINES
#include < cmath >
Circle::Circle(double radius)
{
this->radius = radius;
}
double Circle::getArea(){
return this->radius * this->radius * M_PI;
}</pre>
}}
**main.cpp
#html2(){{
<pre class="brush: cpp;">
#include < iostream >
#include "Rect.h"
#include "Circle.h"
using namespace std;
int main(void)
{
Shape* s;
//s = new Rect(20, 30);
//s = new Circle(10);
cout << "図形の面積は " << s->getArea() << endl;
return 0;
}
</pre>
}}
*問題
**問題1
①「社員」クラスを作り、「働く」という純粋仮想関数を宣言せよ。
②「社員」クラスを継承し「社長」「部長」「営業」クラスを作り、それぞれ中身の異なる「働く」を定義せよ。
③main関数において、「社長」「部長」「営業」のオブジェクトをそれぞれ作れ。
④「社員」配列を用意して、③で作ったオブジェクトを配列に入れよ。
⑤社員配列をforループで回し、社員全員に「働く」関数を実行させよ。
//#include(highlight)
testcounter
合計 &counter(total)
今日 &counter(today)
昨日 &counter(yesterday)
*仮想関数
**仮想関数
スーパークラス側の関数の前に「virtual」とつけることによって、前章の問題は解決します。
前に「virtual」とついた関数のことを仮想関数と呼びます。
仮想関数をオーバーライドした場合、派生クラスの関数が優先されて呼び出されます。
/*--------------Person.hの中身----------------*/
#pragma once
#include < string >
using namespace std;
class Person{
protected:
string name; // 名前
int age; // 年齢
public:
Person(string name, int age); // コンストラクタ
virtual ~Person(); // デストラクタ
virtual void SelfIntroduction() // 自己紹介(仮想関数)
};
/*--------------Student.hの中身----------------*/
#pragma once
#include "Person.h"
class Student : public Person {
protected:
int id; // 学籍番号
public:
Student(string name, int age, int id); // コンストラクタ
~Student(); // デストラクタ
void SelfIntroduction() // 自己紹介(オーバーライド)
int GetID(); // 学籍番号のゲッター
};
/*--------------main.cppの中身----------------*/
#include "Student.h"
int main(){
Person *karasawa = new Person("唐澤", 69); // Personクラス
Student *hasegawa = new Student("長谷川", 24, 241035); // Studentクラス
karasawa->SelfIntroduction(); // 自己紹介(Person)
hasegawa->SelfIntroduction(); // 自己紹介(Student)
return 0;
}
**純粋仮想関数
仮想関数は何かしらの定義を持ちましたが、
定義を持たない仮想関数を作ることもできます。
これを純粋仮想関数と呼び、以下のように = 0 をつけることで実現します。
virtual void SelfIntroduction() = 0;
上のように書くことで定義がないことを表しています。
オーバーライドしなければ使えません。
この純粋仮想関数のみで構成されたクラスを、抽象クラスまたはインターフェースクラスと呼びます。
インターフェースクラスは関数の定義を持っていないため、オブジェクトを作ることができません。
Person *karasawa = new Person("唐澤", 69); // Personクラス【エラー】
必ず継承し、関数を再定義して使うのです。
機能の宣言のみを持ったインターフェースですが、どのようなメリットがあるかつかみにくいと思います。
多態性の章において、基底クラスのポインタには、その派生クラスであればどんなオブジェクトでも扱うことができると書きました。
インターフェースはクラス階層の土台に位置し、抽象的な宣言のみ行います。
クラス使用者はインターフェースのみ知っていれば機能を使うことができる上
インターフェースから派生した様々なオブジェクトを同じコードで処理することができます。
*例
図形の面積を求めるサンプルです。
抽象クラスShapeを用意し
そこから派生したRectクラス、Circleクラスでそれぞれ面積の計算方法を定義します。
**Shape.h
#html2(){{
<pre class="brush: cpp;">
#pragma once
class Shape{
public:
virtual double getArea() = 0;
};
</pre>
}}
**Rect.h
#html2(){{
<pre class="brush: cpp;">
#pragma once
#include "shape.h"
class Rect : public Shape
{
private:
double width;
double height;
public:
Rect(double width, double height);
double getArea();
};
</pre>
}}
**Rect.cpp
#html2(){{
<pre class="brush: cpp;">
#include "Rect.h"
Rect::Rect(double width, double height){
this->width = width;
this->height = height;
}
double Rect::getArea(){
return this->height * this->width;
}</pre>
}}
**Circle.h
#html2(){{
<pre class="brush: cpp;">
#pragma once
#include "shape.h"
class Circle : public Shape
{
private:
double radius;
public:
Circle(double radius);
double getArea();
};
</pre>
}}
**Circle.cpp
#html2(){{
<pre class="brush: cpp;">
#include "Circle.h"
#define _USE_MATH_DEFINES
#include < cmath >
Circle::Circle(double radius)
{
this->radius = radius;
}
double Circle::getArea(){
return this->radius * this->radius * M_PI;
}</pre>
}}
**main.cpp
#html2(){{
<pre class="brush: cpp;">
#include < iostream >
#include "Rect.h"
#include "Circle.h"
using namespace std;
int main(void)
{
Shape* s;
//s = new Rect(20, 30);
//s = new Circle(10);
cout << "図形の面積は " << s->getArea() << endl;
return 0;
}
</pre>
}}
*問題
**問題1
①「社員」クラスを作り、「働く」という純粋仮想関数を宣言せよ。
②「社員」クラスを継承し「社長」「部長」「営業」クラスを作り、それぞれ中身の異なる「働く」を定義せよ。
③main関数において、「社長」「部長」「営業」のオブジェクトをそれぞれ作れ。
④「社員」配列を用意して、「社長」「部長」「営業」のオブジェクトを配列に入れよ。
⑤社員配列をforループで回し、社員全員に「働く」関数を実行させよ。
表示オプション
横に並べて表示:
変化行の前後のみ表示: