4章 分割コンパイル、ビルド

4-1 識別子とC++のキーワード

  • 識別子は、関数や型、変数といった名前に相当するもののこと
  • 概ね下記のルール
    • 大文字小文字は区別
    • 数字で始めることはできない
    • アンダースコアで始まっても良い
    • いくつか使用できないキーワードがある
  • 識別子の付け方の3例
    • スネークケース: アンダースコアで単語区切りを示す。my_test_function
    • アッパーキャメル: 各単語の最初の文字を大文字にする。MyTestFunction
    • ロワーキャメル: 最初の文字だけ小文字に、残りは各単語の最初の文字を大文字にする。myTestFunction
  • Boostや標準ライブラリではスネークケースが、Qtでは両キャメルケースが使われる
  • キーワードは変数、関数名などには使えない
    • true, if, intなど
  • 文脈依存キーワードは変数、関数名などに使えるが、好ましくない
    • final, override
  • 代替表現も変数、関数名などに使えるが、好ましくない
    • and, not, xor, orなど
  • 暗黙的に定義される識別子は下記など
識別子 内容
__FILE__ 文字列リテラル ソースファイル名
__LINE__ 数値リテラル ソースコード__LINE__が出現した行番号
__func__ 文字型の配列 関数名
__cplusplus 数値リテラル c++としてコンパイルされたときのバージョン

4-2 宣言と定義

  • 宣言とは、変数や関数、クラスなどのプログラムを構成する何かの存在を伝えるもの
return-type function-name(parameters, ...); //関数の宣言(プロトタイプ宣言)
struct struct-name;
class class-name;
union union-name;
enum class enum-name;
enum class enum-bame : underlying-type;
  • 関数を事前に宣言しておくことで、定義がプログラムの後ろのほうにあっても関数を呼び出すことができる
    • プログラムの前の方で宣言することを前方宣言とよぶ
  • 定義は関数やクタスの中身がどのようになっているかを記述すること
    • インスタンス化にはそのクラスがどのくらいのサイズのメモリを使用するのか知る必要があるが、宣言だけでは分からない
    • ポインタや参照はアドレス値のみなので、定義がなくても引数、戻り値にできる
      • ただし、定義がないとメンバにアクセスできない
  • ファイル分割を行う場合は、ヘッダファイルに宣言を、ソースファイルに定義を記述するといったテクニックが必要になる
  • クラス定義中にメンバ関数の定義を記載することもできる
    • クラス定義が長くなりやすいので、短い関数に限るべき

4-3 スコープ

  • 変数の有効な範囲をスコープ、変数が有効になっている期間のことを生存期間という
  • 関数定義の{から}までを関数スコープという
    • 関数スコープの中で定義された変数と仮引数は関数から処理が戻るときに破棄される
  • ブレースで囲めばスコープを作れる
  • グローバルスコープmain関数が呼ばれる前から終了するまで
    • global変数はこのスコープが生存期間
    • 関数の外側で定義された変数
  • static変数もプログラム終了するまで生存する
    • global変数もstaticにできるが、その場合はファイルスコープな変数になる
      • つまり、そのソースファイルの中でしか使えない
    • static変数は初期化することができるが、初期化はその宣言にたどり着いた一度目にしか行われない
    • static変数は寿命が長い以外はローカル変数と違いがない
      • つまり、宣言されたスコープでしか使用できない
    • グローバル変数は、どこで使われているか見通しが立てづらいので、static変数でもうまく記述できない場合にのみ使うべき
  • すでに破棄されてしまっている変数をさしたポインタをダングリングポインタとよぶ

4-4 初期化構文付き条件分岐

  • C++17で追加された機能
  • if文, switch文でもセミコロンを使って変数の初期化ができるようになった
    • あまり使わない
if (type_name var_name = init_value; condition)
{
 ...
}

switch (type_name var_name = init_value; condition)
{
 ...
}

4-5 分割コンパイル

  • 1ファイルの場合
    • ソースファイルを実行形式ファイルに変換する手順をコンパイルと呼ぶ
  • 複数ファイルの場合
    • ソースファイルのヘッダファイルを読み込んだり、マクロの展開などの前処理をプリプロセスとよぶ
    • プリプロセス済みソースをオブジェクトファイルに変換することをコンパイルとよぶ
    • オブジェクトファイルを結合して、実行形式ファイルに変換する手順をリンクとよぶ
    • これらをまとめてビルドとよぶ
  • externキーワードをつけると、実体を持たない変数を宣言できる
    • global変数を複数ソースやヘッダファイルで宣言すると、それぞれのオブジェクトで実体をもってしまう
    • グローバルスコープの変数でしか使えない
    • staticな変数なglobal変数はそのソース内でしか使用できないのでexternできない
  • static変数はヘッダで定義してはいけない
    • ソースファイルごとに定義が行われてしまうから
extern type-name var-name; // in global scope