読者です 読者をやめる 読者になる 読者になる

tak0kadaの何でもノート

発声練習、生存確認用。

医学関連は 医学ノート

「C++の絵本」を読んだ - (1章〜5章)

C、C++ 読書メモ

PythonやRなどのスクリプト言語では複雑で時間のかかる処理をC++やCに投げることが多い。C++11は見ている感じでは今までよりもずいぶん書きやすくなっていそうな印象がある。今の隙に勉強しておきたいが、学習コストは低くないので少しずつこまめに勉強するスタイルでいく。

C++の絵本

生協で売っていたC++書籍で一番簡単そうなやつを買った。

1章: 基本

クラスはC言語の構造体を拡張したもので型として考える。変数と関数が入る。

// クラスの定義
class MikanBox {
  public:
    MikanBox(); // コンストラクタ
    ~MikanBox(); // デストラクタ
    void Add(int addmikan);
    void Del(int addmikan);  
  private:
    int total;
}; // ;を忘れやすいので注意

// メンバ関数の定義
MikanBox::MikanBox() {
    std::cout << "コンストラクタだよ〜" << std::endl;
    total = 0;
}

MikanBox::~MikanBox() {
    std::cout << "デストラクタが呼ばれました" << std::endl;
    // デストラクタっぽい処理
}

MikanBox::Add(int addmikan) {
    total += addmikan;
}

int main() {
    // オブジェクトの生成
    MikanBox myMikanBox1;
    myMikanBox.Add(1);
    // エラー
    myMikanBox.total = 1;
}

publicとprivateはアクセス指定子と呼ばれる。publicは他のオブジェクトや普通の関数からアクセスできる。privateは同じオブジェクト内のメンバ関数のみからアクセス可能。

2章: 書き方

プロトタイプ宣言: 先に型だけ宣言して定義は後で書く。関数の呼び出しより前に関数の定義かプロトタイプ宣言をしておく必要がある。(ヘッダの依存関係で重要らしい)

  • 変数は使う直前に定義すればいい。
  • #defineよりconstを使おう。(型の定義、デバッガ対応)
  • 引数の型にconstを付けておけば、誤って変更するのを防げる。(const pointerとconstな値へのpointerは別物)
  • constなポインタはconstでないポインタに代入できない。
  • 列挙型: enum
enum Colors {Red, Green, Blue};
//スコープ付きenum!!
enum class Colors : char {
    Red,
    Green,
    Blue
};

C++11メモ @ enum classでスコープ付きenum

3章: 機能

  • デフォルト引数
//デフォルト引数付き
unsigned int GetSalary(std::string position = "Madogiwa", bool bonus = false);
// エラー: デフォルト値のある値は後ろ
unsigned int GetSalary(std::string position = "Madogiwa", bool bonus);
printf(GetSalary()); // 引数を省略可
printf(GetSalary("Shatyo"));
printf(GetSalary(,true)); // エラー
int MyAdd(int a, int b);
double Sum(double a, double b);
int Sum(double a, double b); // エラー: 戻り値の型だけ違う
double Sum(double a, double b);
double Sum(double a, double b, double c);
#include<iostream>
int main() {
    std::cout.width(4) << 'wan' << std::endl; // " wan"
    std::cout.fill('0');
    std::cout.width(6);
    std::cout.precision(4) << '124.33' << endl; // 0124.3
    return 0;
}
int i;
std::cin >> i;
char name[100];
std::cin.getline(name, sizeof(name));
::hoge; // グローバル変数を優先
  • namespace
namespace std; //以下全てに適応
namespace orange{ // 括弧内のみ
    int GetPrice() {
        return 1;
    }
} // namespace orange
  • inline関数

呼び出しのオーバヘッドが大きくなる時につかう。define マクロより便利(マクロの方は勝手に複数回インクリメントされたりするらしい)。

inline int calc(int a, int b) {
    return a * b;
}

int main() {
    for (int i=1; i<10, i++) {
        for (int j=1; j<10, j++) {
            std::cout << calc(i, j); // ここに展開される
        }
        std::cout << std::endl;
    }
}

4章: 参照

参照はシンボリックリンクのようなもの。

int i = 0;
int &ri = i; // 参照
ri += 1;
std::cout << ri << i << std::endl; // 2 2

関数の引数でも使える。

double AddNum(double& a, double& b);
double AddNum(const double& a, const double& b); // constをつけると元の変数を変更できなくなる
  • ポインタとの使い分け ポインタ渡し、constなしの参照渡しでは引数の値を変更できる(副作用)。変更しない場合はconstつき参照渡し、する場合はポインタ渡しなどとすれば良いかも。

  • this thisはオブジェクトそのものを指すポインタ。(Pythonで言うselfのようなもの)

MikanBox::setname(std::string name){
    this->name = name
}

同じ名前でも明示的に区別できる。(構造体っぽいのでC++のクラスの由来を感じる。)

5章: クラスの構築

  • メモリの確保

CやC++のメモリ管理では、コンパイル時に決定されるスタックと実行時に決定されるヒープを区別する。ヒープ領域にメモリを確保するにはCではmallocで(知らない)、C++ではnewキーワードも使える。 メモリの開放はmalloc()はfree()、newはdeleteで行う。

MikanBox* pMikanBox = new MikanBox; // オブジェクトはnewでのみ生成可能
delete pMikanBox;
int n;
std::cout >> n;
int i = new n;
delete i;
int* arr = new int[n];
delete [] arr; // 配列は[]を付けないと最初の要素だけ開放されてしまう

コンストラクタオーバーロードできる。

MikanBox::MikanBox() {
    total = 0;
}

MikanBox::MikanBox(int n) {
    total = n;
}

コンストラクタを記述しなくても自動的にコンストラクタ(デフォルトコンストラクタ)が用意される。

class Person {
public:
    Person(); // コンストラクタ
    Person(const Person &psn); // コピーコンストラクタ
    ~Person(){
        delete[] name;
    }
private:
    char* name;
};

Person::Person(const Person &psn) {
    name = new char[strlen(psn.name) + 1];
    strcpy(name, psn.name);
}

デストラクタの定義もコピーコンストラクタの定義もstd::string使えば不要なので、この例ではchar使うのやめろよという話ではある。
コンストラクタと同じく、コピーコンストラクタも定義しない場合はデフォルトコンストラクタが用意される。

  • friend

他のクラスのprivate変数(関数)にアクセスできるようにする。

class Info {
friend class OtherInfo;
private:
    int hidden_info;
};

class OtherInfo{
    void Access(Info info) {
        info.hidden_info = 1;
    }
};

乱用するものではない。