見出し画像

C++ 再入門 その8 クラスの書き方

クラスって構造体の親戚だとは説明しました。ですから型を定義してから、その変数を宣言するという手順も同じです。小規模なコードの場合、慣れるまでは同じソースファイルの中で型を定義してから、必要になった場所で変数を宣言して使うことが多いでしょう。

#include <iostream>
#include <string>

using namespace std;

class product {
public:
  product(string sku, int price, string name) {
    sku = m_sku;
    price = m_price;
    name = m_name;
  }
  string to_str() {
    return m_sku + "," + to_string(m_price) + "," + m_name;
  }

  string m_sku;
  int m_price;
  string m_name;
};

void main() {
  product p("001", 1000, "sample");
  cout << p.to_str() << endl;
}

これで商品情報を表すproductクラスを必要な値を使って初期化し、その内容を文字列として出力することが出来ます。

ちなみにクラスに属するメンバ変数の名前ですが、その付け方にはいろいろな流儀があります。メンバ変数であることをわかりやすくするために“m_”で始めたり、”m”で始め次の文字を大文字にすることが多いです。普通の名前だと引き数で渡される変数の名前と被りやすいという事情もあるので一捻りする方が普通です。個人的には先頭の”_”は使ってはいけないので、最後に”_”を付けるのが好みです。

同じクラスをいろいろなファイルで使いたくなるのは良くあることです。そこでクラスはやはり構造体の仲間なので、同じように宣言をヘッダファイルに書いて使う時はそれをインクルードして使うように書き直します。

[product.h]

#pragma once
#include <string>
using namespace std;

class product {
public:
  product(string sku, int price, string name) {
     m_sku = sku;
     m_price = price;
     m_name = name;
   }
   string to_str() {
     return m_sku + "," + to_string(m_price) + "," + m_name;
   }

   string m_sku;
   int m_price;
   string m_name;
};

[main.cpp]

#include <iostream>
#include "product.h"

void main() {
  product p("001", 1000, "sample");
  cout << p.to_str() << endl;
}

C++でヘッダファイルを作る時は、拡張子をC言語と同じ”.h”とすることもありますし(VSのウィザードはそうしている)、C++であることを明示するために”.hpp”を使う人も居ます。但し標準ライブラリのヘッダファイルには拡張子はありません。ここは好きな拡張子で構いません。

こうして定義をヘッダファイルで行うことで、このクラスを使いたい時にはインクルードすれば良くなるわけです。ここで構造体の時には出会わなかった問題が発生します。クラス定義には変数だけではなく関数も含まれています。つまりインクルードするファイルごとに、その関数がコンパイルされて呼び出されることになるのです。関数に含まれるコードが修正されれば当然のようにそれをインクルードしているソースコードは再度コンパイルする必要があります。

そこでクラスに含まれる関数についてはヘッダファイルには、その定義(プロトタイプ)だけを書くことにして、関数の中身はクラス関数だけのファイルに書くことも出来ます。そうすれば関数に含まれるコードに変更があっても、その定義が変わらなければ(インクルードファイルに変更がなければ)クラス関数のファイルだけをコンパイルしなおせば充分です。

その場合の関数の書き方に少しばかりクセがあります。

[product.h]

#pragma once
#include <string>
using namespace std;

class product {
public:
  product(string sku, int price, string name);
  string to_str();

  string m_sku;
  int m_price;
  string m_name;
};

[product.cpp]

#include <iostream>
#include "product.h"

product::product(string sku, int price, string name) {
  m_sku = sku;
  m_price = price;
  m_name = name;
}

string product::to_str() {
  return m_sku + "," + to_string(m_price) + "," + m_name;
}

[main.cpp]

#include <iostream>
#include "product.h"

void main() {
  product p("001", 1000, "sample");
  cout << p.to_str() << endl;
}

ヘッダファイルでは関数定義の部分をプロトタイプ宣言と同じように中身を書かずに”;”だけで終わらせます。そしてこの関数の中身を書く時には、関数名の前に(戻り値の型の後に)”クラス名::”を書きます。例えばこの場合のコンストラクタであれば、product::product(string sku, int price, string name) となり、ここから普通の関数と同じように中身を書いていきます(コンストラクタは値を返せないので戻り値の型を書く場所には何も書かない※voidも書いてはいけない)。

さてクラスの関数はヘッダには中身を書かず、すべて別のソースファイルに書くのが良いのかと言うと、実は理由があってケースバイケースです。一般的には処理が単純で短い場合にはヘッダに中身まで書いてしまい、複雑な処理をしている場合には別にしたほうが良いということだけ知っていれば充分です。C++は最適化してこそ本領を発揮する言語なので、コンパイラがどうコードを最適化して効率よく実行できるバイナリを生成できるかに深く関わってくる話が、ここに関係してきたりするのです。

ということで、次回は情報の隠蔽という何やら秘密めいたお話を予定しています。

ヘッダ画像は、以下のものを使わせていただきました。
https://commons.wikimedia.org/wiki/File:ISO_C%2B%2B_Logo.svg
Jeremy Kratz - https://github.com/isocpp/logos, パブリック・ドメイン, https://commons.wikimedia.org/w/index.php?curid=62851110による

#C++ #クラス定義 #ヘッダファイル #メンバ変数 #関数の宣言と中身 #プロトタイプ


この記事が気に入ったらサポートをしてみませんか?