【C++言語入門】 第10回 オーバーライドとポリモーフィズム
基底クラスのメソッドを上書きしましょう
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
const double kPI = 3.14159265358979323846;
/// @brief 円
class Circle {
double diameter_; // 直径
public:
Circle(double diameter) : diameter_(diameter) {}
~Circle() {}
double Diameter() { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
double Area() {
double radius = Diameter() / 2.0;
double a = radius * radius * kPI;
return a;
}
};
#endif // CIRCLE_H
#pragma once
#ifndef ELLIPSE_H
#define ELLIPSE_H
#include "circle.h"
/// @brief 楕円
class Ellipse : public Circle {
double long_diameter_; // 長径
public:
Ellipse(double short_diameter, double long_diameter)
: Circle(short_diameter), long_diameter_(long_diameter) {}
~Ellipse() {}
double LongDiameter() { return long_diameter_; }
void LongDiameter(double long_diameter) { long_diameter_ = long_diameter; }
double Area() {
double short_radius = Diameter() / 2.0;
double long_radius = LongDiameter() / 2.0;
double a = short_radius * long_radius * kPI;
return a;
}
};
#endif // ELLIPSE_H
#include <iostream>
#include "ellipse.h"
int main(int argc, char* argv[]) {
Ellipse* pe = new Ellipse(10.0, 15.0);
std::cout << "楕円の長径は" << pe->LongDiameter() << "cmです。" << std::endl;
std::cout << "楕円の短径は" << pe->Diameter() << "cmです。" << std::endl;
std::cout << "楕円の面積は" << pe->Area() << "cm^2です" << std::endl;
delete pe;
pe = nullptr;
return 0;
}
double Area()
というメソッドがCircle
とその派生クラスEllipse
で定義されているのだ
C++言語の重要な概念ポリモーフィズム
Area
と云うメソッドでもCircle
クラスのインスタンスで呼ばれたか、Ellipse
クラスのインスタンスで呼ばれたかによって面積の計算方法が異なりますわね
#include <iostream>
#include "ellipse.h"
int main(int argc, char* argv[]) {
Ellipse* pe = new Ellipse(10.0, 15.0);
Circle* pc = pe;
std::cout << "楕円の長径は" << pe->LongDiameter() << "cmです。" << std::endl;
std::cout << "楕円の短径は" << pe->Diameter() << "cmです。" << std::endl;
std::cout << "楕円の面積は" << pe->Area() << "cm^2です" << std::endl;
std::cout << "円の面積は" << pc->Area() << "cm^2です" << std::endl;
delete pe;
pc = nullptr;
pe = nullptr;
return 0;
}
Ellipse
クラスとして生成したインスタンスをCircle
クラスのポインタに代入しているのだ
Ellipse
クラスはCircle
クラスを 継承 していますので、Circle
クラスの全てを内包していますわ
Ellipse
クラスのインスタンスはCircle
クラスのインスタンスとして扱っても問題ないのですわ
Circle
のポインタ"pc"からArea
メソッドを呼び出すとCircle
クラス内で定義されたArea
メソッドが呼び出されますわ
元のクラスのメソッドを呼び出したいのですが...
Ellipse
のインスタンスとして生成したのだから、Circle
のポインタから呼び出しても上書きしたEllipse
クラスのArea
メソッドが呼び出されてほしいのだ
virtual
キーワードですわ
virtual
?
virtual 型 メソッド名(引数...) {処理}
virtual
キーワードを付加されたメソッドを 仮想関数 もしくは 仮想メソッド と云いますわ
virtual
キーワードは 基底クラス でのみ付加すればOKですわ
virtual
キーワードを使ってみましょう
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
const double kPI = 3.14159265358979323846;
/// @brief 円
class Circle {
double diameter_; // 直径
public:
Circle(double diameter) : diameter_(diameter) {}
~Circle() {}
double Diameter() { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
virtual double Area() {
double radius = Diameter() / 2.0;
double a = radius * radius * kPI;
return a;
}
};
#endif // CIRCLE_H
#pragma once
#ifndef ELLIPSE_H
#define ELLIPSE_H
#include "circle.h"
/// @brief 楕円
class Ellipse : public Circle {
double long_diameter_; // 長径
public:
Ellipse(double short_diameter, double long_diameter)
: Circle(short_diameter), long_diameter_(long_diameter) {}
~Ellipse() {}
double LongDiameter() { return long_diameter_; }
void LongDiameter(double long_diameter) { long_diameter_ = long_diameter; }
virtual double Area() {
double short_radius = Diameter() / 2.0;
double long_radius = LongDiameter() / 2.0;
double a = short_radius * long_radius * kPI;
return a;
}
};
#endif // ELLIPSE_H
Circle
のインスタンスから呼ばれたArea
メソッドはEllipse
のメソッドとなったのだ
デストラクタも仮想メソッドに
Ellipse
のインスタンスを破棄するためにEllipse
へのポインタに対してdelete
していますわ
Circle
へのポインタ"pc"に対してdelete
することはできるのでしょうか?
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
const double kPI = 3.14159265358979323846;
/// @brief 円
class Circle {
double diameter_; // 直径
public:
Circle(double diameter) : diameter_(diameter) {}
~Circle() { std::cout << "~Circleが呼ばれました" << std::endl; }
double Diameter() { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
virtual double Area() {
double radius = Diameter() / 2.0;
double a = radius * radius * kPI;
return a;
}
};
#endif // CIRCLE_H
#pragma once
#ifndef ELLIPSE_H
#define ELLIPSE_H
#include "circle.h"
/// @brief 楕円
class Ellipse : public Circle {
double long_diameter_; // 長径
public:
Ellipse(double short_diameter, double long_diameter)
: Circle(short_diameter), long_diameter_(long_diameter) {}
~Ellipse() { std::cout << "~Ellipseが呼ばれました" << std::endl; }
double LongDiameter() { return long_diameter_; }
void LongDiameter(double long_diameter) { long_diameter_ = long_diameter; }
virtual double Area() {
double short_radius = Diameter() / 2.0;
double long_radius = LongDiameter() / 2.0;
double a = short_radius * long_radius * kPI;
return a;
}
};
#endif // ELLIPSE_H
#include <iostream>
#include "ellipse.h"
int main(int argc, char* argv[]) {
Ellipse* pe = new Ellipse(10.0, 15.0);
Circle* pc = pe;
std::cout << "楕円の長径は" << pe->LongDiameter() << "cmです。" << std::endl;
std::cout << "楕円の短径は" << pe->Diameter() << "cmです。" << std::endl;
std::cout << "楕円の面積は" << pe->Area() << "cm^2です" << std::endl;
std::cout << "円の面積は" << pc->Area() << "cm^2です" << std::endl;
delete pc;
pc = nullptr;
pe = nullptr;
return 0;
}
Circle
のデストラクタは呼ばれてもEllipse
のデストラクタは呼ばれていないのだ
Ellipse
のインスタンスなので、Ellipse
のデストラクタが呼ばれないとメモリリークになってしまいますわ
delete
をコールした際にデストラクタが 仮想メソッド として指定されていないためにCircle
のデストラクタが直接呼ばれることで発生していますわ
virtual
キーワードを付加して仮想化する必要がありますわ
#pragma once
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
const double kPI = 3.14159265358979323846;
/// @brief 円
class Circle {
double diameter_; // 直径
public:
Circle(double diameter) : diameter_(diameter) {}
virtual ~Circle() { std::cout << "~Circleが呼ばれました" << std::endl; }
double Diameter() { return diameter_; }
void Diameter(double diameter) { diameter_ = diameter; }
virtual double Area() {
double radius = Diameter() / 2.0;
double a = radius * radius * kPI;
return a;
}
};
#endif // CIRCLE_H
#pragma once
#ifndef ELLIPSE_H
#define ELLIPSE_H
#include "circle.h"
/// @brief 楕円
class Ellipse : public Circle {
double long_diameter_; // 長径
public:
Ellipse(double short_diameter, double long_diameter)
: Circle(short_diameter), long_diameter_(long_diameter) {}
virtual ~Ellipse() { std::cout << "~Ellipseが呼ばれました" << std::endl; }
double LongDiameter() { return long_diameter_; }
void LongDiameter(double long_diameter) { long_diameter_ = long_diameter; }
virtual double Area() {
double short_radius = Diameter() / 2.0;
double long_radius = LongDiameter() / 2.0;
double a = short_radius * long_radius * kPI;
return a;
}
};
#endif // ELLIPSE_H
Circle
とEllipse
のデストラクタが順番に呼び出されたのだ
virtual
を付けてくださいね
Discussion