【プログラミング言語Ⅳ】講義メモ 7.クラス

7. クラス:7.1 データメンバ
 https://rinatz.github.io/cpp-book/ch07-01-data-members/

・クラス:構造体の上位概念で、変数と関数を集約した型をつくるための仕組み
・なお、C/C++の構造体は変数を集約した型をつくるための仕組み
 ※ C#の構造体は軽量のクラスなので、C/C++の構造体とは意味が異なる
・メンバ:クラスの構成要素のことで、主に変数と関数
・データメンバ:クラスが持つ変数で、メンバ変数ともいう
・クラスが持つメンバは基本的にクラス内でのみ有効で、外部からアクセスできないが「public:」以降のメンバはアクセス可能
・クラスの基本的な定義書式: class クラス名 { メンバの定義; … }; //末尾にセミコロンが必要
・外部からアクセスできるデータメンバを持つクラスの書式例:
class クラス名 { 
 public:
 データメンバの型 データメンバ名;
 :
}
・データメンバを用いる側では、クラスを型とする変数を定義し、データメンバ名の前に「変数名.」を前置することで、
 通常の変数と同様に利用できる
 書式例: クラス名 変数名; 変数名.データメンバ名 = 値;
・なお、C++ではクラスを型とする変数を定義した時点で、クラスの実体であるインスタンスが自動生成される
 (この場合、C#の様にnewで生成する必要はない)

演習:データメンバ その1 project1/p070101.cpp

・データメンバのサンプルソース(「データメンバを参照するには」まで)を動作可能にしよう
・データメンバ値の表示を追加すること
・クラス定義はmain関数の外に置くと良い

作成例

//演習:データメンバ その1 project1/p070101.cpp
#include <iostream>
class Rectangle { //四角形クラスの定義
public: //これ以降は公開メンバ
    int height_; //データメンバ(高さ)
    int width_; //データメンバ(幅)
};
int main() {
    Rectangle r; //クラスを型とする変数の宣言(インスタンスが生成される)
    r.height_ = 10; //データメンバ(高さ)に代入
    r.width_ = 20; //データメンバ(幅)に代入
    std::cout << " height = " << r.height_ << std::endl; //データメンバ(高さ)の値を表示
    std::cout << " width = " << r.width_ << std::endl; //データメンバ(幅)の値を表示
    return 0;
}

7. クラス:7.1 データメンバ(つづき)

・クラスには大量のデータメンバや要素数の大きな配列を記述できる
・すると、インスタンスも大容量になるので、変数ではなくポインタで扱うと効率的
・クラスのオブジェクト(インスタンス)をポインタで扱うには、変数の場合と同様に「*」と「&」を用いる
 書式例: クラス名* ポインタ名 = &変数名;
・データメンバをポインタ経由で参照するには「変数名.」を「(*ポインタ名).」とする
 ※メンバ演算子「.」がポインタ解決演算子「*」より優先度が高いのでカッコが必要
・この記法は見づらく、ミスの元なので、アロー演算子「->」を用いて「ポインタ名->メンバ名」とすれば良い

演習:データメンバ その2 project1/p070102.cpp

・データメンバのサンプルソース(「(*r). の代わりに r->」)を動作可能にしよう
・データメンバ値の表示を追加すること

作成例

//演習:データメンバ その2 project1/p070102.cpp
#include <iostream>
class Rectangle { //四角形クラスの定義
public: //これ以降は公開メンバ
    int height_; //データメンバ(高さ)
    int width_; //データメンバ(幅)
};
int main() {
    Rectangle rectangle; //クラスを型とする変数の宣言(インスタンスが生成される)
    Rectangle* r = &rectangle; //インスタンスを指すポインタを定義
    r->height_ = 10; //データメンバ(高さ)にポインタ経由で代入
    r->width_ = 20; //データメンバ(幅)にポインタ経由で代入
    std::cout << " height = " << r->height_ << std::endl; //ポインタ経由で値を表示
    std::cout << " width = " << r->width_ << std::endl; //ポインタ経由で値を表示
    return 0;
}

7. クラス:7.2 メンバ関数

・クラスには(構造体とは異なり)メンバとして関数を定義できる
・これにより、データとデータを扱う処理をまとめて扱えること(オブジェクト指向・部品の基礎)が可能
・メンバ関数の書式は通常の関数と同様だが、定義の上下関係による呼び出しや利用の可否はない
・よって、あるメンバ関数の定義位置より下で定義しているデータメンバや他のメンバ関数を利用・呼び出し可能
・メンバ関数を用いる側では、クラスを型とする変数を定義し、メンバ関数名の前に「変数名.」を前置することで、
 通常の関数と同様に利用できる

演習:メンバ関数 project1/p070201.cpp

・メンバ関数のサンプルソースを動作可能にしよう
・メンバ関数Area()の戻り値を表示する処理を追記すること

作成例

//演習:メンバ関数 project1/p070201.cpp
#include <iostream>
class Rectangle { //四角形クラスの定義
public: //これ以降は公開メンバ
    int Area() { //メンバ関数「面積」
        return height_ * width_; //下方で定義しているデータメンバを利用
    }
    int height_; //データメンバ
    int width_; //データメンバ
};
int main() {
    Rectangle r; //クラスを型とする変数の宣言(インスタンスが生成される)
    r.height_ = 10; //データメンバ(高さ)に代入
    r.width_ = 20; //データメンバ(幅)に代入
    std::cout << "面積 = " << r.Area() << std::endl; //メンバ関数を呼んで戻り値を表示
}

7. クラス:7.2 メンバ関数:クラス宣言とは別に定義

・クラス宣言の中に関数定義を記述するとクラス定義の内容が長文化して見通しが悪くなる
・そこで、クラス定義の中にはメンバ関数のプロトタイプ宣言を置き、メンバ関数の内容は別に定義することが可能
・別に定義する場合は、メンバ関数名の前に「クラス名::」を前置すること

演習:メンバ関数:クラス宣言とは別に定義 project1/p070202.cpp

・「クラス宣言とは別に定義」のサンプルソースを動作可能にしよう
・メンバ関数Area()の戻り値を表示する処理を追記すること
・また、別に定義するRectangle::Area()の記述場所が自由であることも確認しよう
 ⇒ クラス定義の後であれば、main()関数の前でも後でも良い

作成例

//演習:メンバ関数:クラス宣言とは別に定義 project1/p070202.cpp
#include <iostream>
class Rectangle { //四角形クラスの定義
public: //これ以降は公開メンバ
    int Area(); //メンバ関数「面積」のプロトタイプ宣言
    int height_; //データメンバ
    int width_; //データメンバ
};
int Rectangle::Area() { //メンバ関数「面積」の定義をクラスの外で行う
    return height_ * width_; //下方で定義しているデータメンバを利用
}
int main() {
    Rectangle r; //クラスを型とする変数の宣言(インスタンスが生成される)
    r.height_ = 10; //データメンバ(高さ)に代入
    r.width_ = 20; //データメンバ(幅)に代入
    std::cout << "面積 = " << r.Area() << std::endl; //メンバ関数を呼んで戻り値を表示
}

7. クラス:7.2 メンバ関数:暗黙的な inline 指定

・inline 指定については「4.5 inline関数」を参照
 https://rinatz.github.io/cpp-book/ch04-05-inline-functions/
・クラス宣言の中でメンバ関数を定義した場合、暗黙的にinline指定が行われる
・これにより、クラスの定義をヘッダ内で行い、その中でメンバ関数を定義しても良い。

演習:メンバ関数:暗黙的な inline 指定 project070203/rectangle.h,something.h,something.cpp,main.cpp

・「暗黙的な inline 指定」前半のサンプルソースの動作を確認しよう
・4ソースで構成しているので、専用のプロジェクトを作成すると良い

作成例

//演習:メンバ関数:暗黙的な inline 指定 project070203/rectangle.h
#ifndef RECTANGLE_H_
#define RECTANGLE_H_
class Rectangle { //四角形クラスの定義
public: //これ以降は公開メンバ
    int Area() { //メンバ関数「面積」
        return height_ * width_; //下方で定義しているデータメンバを利用
    }
    int height_; //データメンバ
    int width_; //データメンバ
};
#endif  // RECTANGLE_H_

//演習:メンバ関数:暗黙的な inline 指定 project070203/something.h
#ifndef SOMETHING_H_
#define SOMETHING_H_
void Something(); //通常関数のプロトタイプ宣言
#endif  // SOMETHING_H_

//演習:メンバ関数:暗黙的な inline 指定 project070203/something.cpp
#include "something.h" //自前のヘッダを利用
#include <iostream>
#include "rectangle.h" //クラス定義を含むヘッダを利用
void Something() { //ヘッダで宣言している関数の内容を記述
    Rectangle r; //クラスを型とする変数の宣言(インスタンスが生成される)
    r.height_ = 2; //データメンバ(高さ)に代入
    r.width_ = 3; //データメンバ(幅)に代入
    std::cout << r.Area() << std::endl; //メンバ関数を呼んで戻り値(面積)を表示
}

//演習:メンバ関数:暗黙的な inline 指定 project070203/main.cpp
#include <iostream>
#include "rectangle.h" //クラス定義を含むヘッダを利用
#include "something.h" //自前のヘッダを利用
int main() {
    Rectangle r; //クラスを型とする変数の宣言(インスタンスが生成される)
    r.height_ = 10; //データメンバ(高さ)に代入
    r.width_ = 20; //データメンバ(幅)に代入
    std::cout << r.Area() << std::endl; //メンバ関数を呼んで戻り値(面積)を表示
    Something(); //別ソース(something.cpp)で定義済の関数を呼ぶ
    return 0;
}

7. クラス:7.2 メンバ関数:暗黙的な inline 指定と「クラス宣言とは別に定義」

・クラス宣言とは別にメンバ関数を定義すると暗黙的な inline 指定はされなくなる
・よって、ヘッダファイル内のクラス宣言とは別にメンバ関数の定義を行うとリンク時にエラーとなる
・ヘッダファイル内でクラス宣言とは別にメンバ関数の定義を行うには、明示的に inline 指定する必要がある

提出:演習:メンバ関数:暗黙的な inline 指定 project070204/rectangle.h,something.h,something.cpp,main.cpp

・「暗黙的な inline 指定」後半のサンプルソースの動作を確認しよう
・inline指定をしていない場合にどうなるかを確認してから、inlineを追記しよう
・4ソースで構成しているので、専用のプロジェクトを作成すると良い
※ 4ソースを1つのZIPファイルに圧縮して「提出フォーム」から提出すること
※ rectangle.hのみでも良いが、圧縮して「提出フォーム」から提出すること

コメントを残す

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