【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