アフィリエイト広告を利用しています

広告

この広告は30日以上更新がないブログに表示されております。
新規記事の投稿を行うことで、非表示にすることが可能です。
posted by fanblog

2017年12月25日

《その205》 抽象クラス(3)


 抽象クラス

 下記のプログラムでは、抽象クラス Aaa は3つの純粋仮想関数 set, get, func を持っています。
純粋仮想関数は、関数 set, 関数 get のように、関数本体を与えずに記述するのが一般的ですが、
関数 func のように関数本体を定義することも可能です(★1.★2.)。

 関数 Aaa::func は、関数 set と 関数 get を連続して呼び出します。
純粋指定子「 = 0 」★1. を付ける都合上、関数定義をクラス定義の外に記述してあります★2.


 また、抽象クラス Aaa のデストラクタ ~Aaa ★3. は virtual の付いた仮想デストラクタになっています。
virtual無しの場合は、プログラム終了時に、デストラクタ ~Bbb が呼ばれず、 動的に確保 ★4. した領域が解放されないままになってしまいます。


// ------------------------------------
#include <iostream>

class Aaa {
public:
virtual ~Aaa() { // ★3.
std::cout << "~Aaa が呼ばれました。\n";
}
virtual void set() = 0;
virtual void get() const = 0;
virtual void func() = 0; // ★1.
};

void Aaa::func() { // ★2.
set(); get();
}


class Bbb : public Aaa{
int n;
int* p;
public:
Bbb(int x) : n(x) {
p = new int[n]; // ★4.
}

~Bbb() {
delete[] p;
std::cout << "~Bbb が呼ばれました。\n";
}

void set() {
for (int i = 0; i < n; i++)
p[i] = i;
}

void get() const {
for (int i = 0; i < n; i++)
std::cout << p[i] << " ";
std::cout << '\n';
}

void func() { Aaa::func(); }
};


int main() {
Aaa* p = new Bbb(5);

p->func();
delete p;
}
// ------------------------------------

f06_0003.png


 もし、★3.の箇所で、デストラクタ ~Aaa に virtual が付いていない場合は、下のような結果になります。
デストラクタ ~Bbb が呼ばれていないことが確認できます。
f06_0004.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その204》 抽象クラス(2)


 抽象クラス

 前回《203》、仮想関数からの派生関数 Rectangle を作成しました。
Rectangl関数は四角形クラスでしたが、今回は、横線クラス HorzLine と縦線クラス VertLine を作成します。

  ※ '-' で描く横線クラス  … HorzLine
  ※ '|' で描く縦線クラス  … VertLine


 この2つの関数には、"線"という共通点があります。"線"ならば、その線の長さをデータメンバとして持っていなければなりません。

 そこで、まず、直線クラス Line を考えます。線を描く方法は、横線と縦線で異なるので関数 draw は、ここではまだ定義できません。

  class Shape {
  public:
    virtual void draw() = 0;
  };

  class Line : public Shape {
    int length;  // 線の長さ
  public:
  };



 この Lineクラスは、Shapeクラスの public派生クラスですが、
基底クラス Shape の純粋仮想関数 draw をオーバーライドしていません。
そのため、Lineクラスにおいても、draw は純粋仮想関数のままです。

 純粋仮想関数を有するクラスは、抽象クラスです。

したがって、Lineクラスは、まだ抽象クラスです。


 次のプログラムでは、Lineクラスの public派生クラスとして、
横線クラス HorzLine,縦線クラス VertLine を定義しています。


// ------------------------------------
#include <iostream>

// 図形クラス(抽象クラス) Shape
class Shape {
public:
virtual ~Shape() = 0;
// 純粋仮想デストラクタ

virtual void draw() const = 0;
// 描画
// 純粋仮想関数

};

inline Shape::~Shape() { }

// 長方形クラス Rectangle
class Rectangle : public Shape {
int width; // 横幅
int height; // 高さ

public:
Rectangle(int w, int h)
: width(w), height(h) { }

void draw() const {
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= width; j++)
std::cout << '*';
std::cout << '\n';
}
}
};

// 直線クラス(抽象クラス) Line
class Line : public Shape {
protected:
int length; // 線の長さ
public:
Line(int len) : length(len) { }
};


// 横線クラス HorzLine
class HorzLine : public Line {
public:
HorzLine(int len) : Line(len) { }

void draw() const {
for (int i = 1; i <= length; i++)
std::cout << '-';
std::cout << '\n';
}
};

// 縦線クラス VertLine
class VertLine : public Line {
public:
VertLine(int len) : Line(len) { }

void draw() const {
for (int i = 1; i <= length; i++)
std::cout << "|\n";
}
};

int main() {
Rectangle a(5, 3);
HorzLine c(20);
VertLine d(3);

a.draw(); std::cout << '\n';
c.draw(); std::cout << '\n';
d.draw();
}
// ------------------------------------

f06_0002.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その203》 抽象クラス(1)


 抽象クラス

 次のような、簡単なクラスを作るものとします。

  ※ '-' で描く横線クラス  … HorzLine
  ※ '|' で描く縦線クラス  … VertLine
  ※ '*' で描く四角形クラス … Rectangle


 これらはすべて、図形ですから、まず具体的な個々の形には踏み込まずに、抽象的な

   図形クラス … Shape

を作成することにします。
とは言っても、図形というだけでは、形に関してはまだ何もわかりません。ひとつ確かなことは、図形なら、"描く"作業が必ず必要なはずです。

そこで、次のようなクラスを作ってみます。draw関数は、"描く"ための関数(関数の中身は空っぽですが)です。

  class Shape {
  public:
    virtual void draw() = 0;
    // " = 0 " は純粋指定子
    //   この純粋指定子を付けて宣言された
    //   仮想関数は、純粋仮想関数となりま
    //   す。
  };


 【 抽象クラス

 クラス Shape には具体的な情報が含まれていないので、Shape型のオブジェクトを作ることは不可能です。
この Shape のようなクラスは抽象クラスと呼ばれ、具体的な図形クラスは、このクラスからの派生で実現するようにします。



 次のプログラムでは、抽象クラス Shape から派生した Rectangleクラスの長方形オブジェクト a, b を生成して、それぞれのオブジェクトの draw関数で長方形を描いています。


// ------------------------------------
#include <iostream>

// 抽象クラス Shape
class Shape {
public:
virtual ~Shape() = 0;
// 純粋仮想デストラクタ

virtual void draw() const = 0;
// 描画
// 純粋仮想関数
};

inline Shape::~Shape() { }

// 長方形クラス Rectangle
// (抽象クラス Shape から派生)

class Rectangle : public Shape {
int width; // 横幅
int height; // 高さ

public:
Rectangle(int w, int h)
: width(w), height(h) { }

void draw() const {
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= width; j++)
std::cout << '*';
std::cout << '\n';
}
}
};

int main() {
Rectangle a(5, 3);
Rectangle b(3, 4);

Shape& ref = b;
// Rectangle& ref = b; としても結果は同じで
// すが、図形の種類が増えてきて、様々な
// 処理をしなけらばならなくなったときの
// 効率を考えれば、Shape& とすべきです。


a.draw();
std::cout << '\n';
ref.draw();
}
// ------------------------------------

f06_0001.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その202》 仮想関数のいちばん基礎的な部分 のおさらい


 仮想関数に関する、いちばん基礎的な事項を、
下記の2つのプログラムと プログラム中の注記で おさらいしてみました •̀д•́;)/


@【仮想関数の利用無し】の場合
// ------------------------------------
#include <iostream>
using namespace std;

/* 基底クラス */
class Aaa {
int a;
public:
Aaa(int x = 10) : a(x) { }
void get() { cout << a << '\n'; }
};

/* 派生クラス(クラス Aaa を public継承) */
class Bbb : public Aaa {
int b;
public:
Bbb(int x = 20, int y = 99)
: Aaa(x), b(y) { }
void get() { cout << b << '\n'; }
// 基底クラスの get と関数名が同じなので、
// 基底クラスの get は隠蔽される。

};

int main()
{
Bbb bbb;
// Bbb型オブジェクト bbb を生成。

Aaa& ref = bbb;
// クラス Bbb は多相的クラスではないので、
// ref は bbb への静的な Aaa&型参照。


ref.get(); // 20 が表示される。

bbb.get(); // 99 が表示される。
// 基底クラスの get は隠蔽されているので、
// 派生クラス内で定義された get
// が呼び出される。


bbb.Aaa::get(); // 20 が表示される。
// 基底クラス Aaa から継承した get関数に
// アクセス。

}
// ------------------------------------


A【仮想関数の利用有り】の場合
// ------------------------------------
#include <iostream>
using namespace std;

/* 基底クラス */
class Aaa {
int a;
public:
Aaa(int x = 10) : a(x) { }
virtual void get() { cout << a << '\n'; }
// virtual を付けて仮想関数にする。
// クラス Bbb の 関数 get も自動的
// に仮想関数になる
// クラス Aaa, Bbb は、仮想関数をメンバ
// として持つので、多相的クラスです。

};

/* 派生クラス(クラス Aaa を public継承) */
class Bbb : public Aaa {
int b;
public:
Bbb(int x = 20, int y = 99)
: Aaa(x), b(y) { }
void get() { cout << b << '\n'; }
// 基底クラスの get と関数名・仮引数が同
// じ関数を、派生クラス内で定義。
// このことを、「オーバライドする」と
// 表現します。
// オーバライドした関数は、
// オーバライダと呼ばれます。

};

int main()
{
Bbb bbb;
// Bbb型オブジェクト bbb を生成。

Aaa& ref = bbb;
// クラス Bbb は多相的クラスなので、
// Aaa&型参照 ref の動的な型は Bbb&型


ref.get(); // 99 が表示される。

bbb.get(); // 99 が表示される。
// 基底クラスの get は隠蔽されているので、
// 派生クラス内で定義された get
// が呼び出される。


bbb.Aaa::get(); // 20 が表示される。
// 基底クラス Aaa から継承した get関数に
// アクセス。

}
// ------------------------------------


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)






 たまに、クリック お願いします m(_ _)m

 AA にほんブログ村 IT技術ブログ C/C++へ

こうすけ:メール kousuke_cpp@outlook.jp

【1】★★C++ 記事目次★★ ← 利用可能です。
・新版明解C++入門編 / 新版明解C++中級編
・その他 C++ 関連記事

【2】★★こうすけ@C#★★
・C# の初歩的な記事


検索
<< 2017年12月 >>
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
プロフィール
こうすけさんの画像
こうすけ

 たまに、クリック お願いします m(_ _)m

 AA にほんブログ村 IT技術ブログ C/C++へ

こうすけ:メール kousuke_cpp@outlook.jp

【1】★★C++ 記事目次★★ ← 利用可能です。
・新版明解C++入門編 / 新版明解C++中級編
・その他 C++ 関連記事

【2】★★こうすけ@C#★★
・C# の初歩的な記事


×

この広告は30日以上新しい記事の更新がないブログに表示されております。