C++   定数

ホーム   C/C++チュートリアル


  1. この章では、define と enum と const について説明しています。
  2. どれも「定数」と呼ばれる、値の変わらないものを定義します。
  3. 定数は、
    • 値を人間がわかりやすい文字列に変更する
    • 複雑なコードを簡単な文字列で置き換える
    • 安全性のために値が変更されないようにする
    などの目的で使われます。
  4. 定数は、一度定義された値を後から変えることはできません。

define

define.cpp


#include <iostream>

#define NUMBER 256
#define HELLO(name) std::cout << "Hello " << name << "." << std::endl;

int main()
{
    std::cout << NUMBER << std::endl;
    HELLO("Jane")
    HELLO(NUMBER)
    
    return 0;
}
    

実行結果


256
Hello Jane.
Hello 256.
    

コード説明

  1. 「#define 半角空白 名前 半角空白 値もしくは処理」 と記述すると「名前」に「値や処理」を結びつけることができます。
  2. 半角空白の部分は半角であれば幾つでも構いません。タブもOKです。 改行したい場合については後述します。
  3. 「名前」の部分は大文字とアンダースコア( _ )で記述する慣習になっています。
  4. 一般的に、define で、値と結びつけたものを「定数」、引数を受け取り一連の処理と結びつけたものを「マクロ」と呼びます。
  5. 「乱数」で登場してきた RAND_MAX もこのサンプルの NUMBER と同じようにして定義 されています。


define の部分は別のファイルとして分けることができます。

define.h


#define NUMBER 256
#define HELLO(name) std::cout << "Hello " << name << "." << std::endl;
    

そして、define.cpp を次のように変更します。

define.cpp


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

int main()
{
    std::cout << NUMBER << std::endl;
    HELLO("Jane")
    HELLO(NUMBER)
    
    return 0;
}
    

コンパイルの方法は同じです。


g++ define.cpp
    

複数の .cpp ファイルがあるときは、g++の後に、空白で区切って .cpp ファイルを連記しますが、ヘッダーファイル(.h)は記述しません。

実行結果


256
Hello Jane.
Hello 256.
    


さらに、プログラムのスターティングポイントである main( )関数全体も define することもできます。

define.h


#define HELLO(name) int main()\
                    {\
                        std::cout << "Hello " << name << "." << std::endl;\
                        return 0;\
                    }
    

define.cpp


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

HELLO("John")
    

実行結果


Hello John.
    

コード説明

  1. define の中で改行したい場合は、\ もしくは ¥ をつけます。
  2. wxWidgets で有名な、「IMPLEMENT_APP( )」 もこのようにして定義されていると思われます。

enum

enum.cpp


#include <iostream>

enum DIRECTION
{
    LEFT    = 101,
    TOP     = 202,
    RIGHT   = 303,
    BOTTOM  = 404,
};

int main()
{
    std::cout << LEFT   << "\t";
    std::cout << TOP    << "\t";
    std::cout << RIGHT  << "\t";
    std::cout << BOTTOM << std::endl;
    
    return 0;
    

実行結果


101	202	303	404
    

コード説明

  1. enum の定義は
    enum 名前 { 個別の名前 = 値, 個別の名前 = 値, ... };
    と記述します
  2. 個別の名前のことを「列挙子」と呼びます。
  3. 列挙子には、整数の初期値を設定できます。整数以外は設定できません。
  4. 整数であれば、0 や -1 なども設定できます。 違う列挙子に同じ値を設定することもできます。
  5. enum の定義後は、列挙子の値は変更できません。
  6. wxWidgets では、列挙子や定数やマクロの名前に、wxID_ANY や wxID_EXIT や wxT と言うように先頭に wx がついている場合が多いです。
  7. enum とは関係ありませんが「\t」はエスケープ文字と呼ばれるものです。 \t はタブ文字として扱われます。


列挙子に、初期値が与えられない場合は、0からの連番になります。

enum2.cpp


#include <iostream>

enum DIRECTION
{
    LEFT    ,
    TOP     ,
    RIGHT   ,
    BOTTOM  ,
};

int main()
{
    std::cout << LEFT   << "\t";
    std::cout << TOP    << "\t";
    std::cout << RIGHT  << "\t";
    std::cout << BOTTOM << std::endl;
    
    return 0;
}
    

実行結果


0	1	2	3
    


最初の列挙子や、途中の列挙子に、特定の値を設定すると、次の列挙子は、その値の +1 になります。

enum3.cpp


#include 

enum DIRECTION
{
    LEFT    = 100,
    TOP     ,
    RIGHT   = 200,
    BOTTOM  ,
};

int main()
{
    std::cout << LEFT   << "\t";
    std::cout << TOP    << "\t";
    std::cout << RIGHT  << "\t";
    std::cout << BOTTOM << std::endl;
    
    return 0;
}
    

実行結果


100	101	200	201
    


enum もファイルを分けることができます。

enum.h


enum DIRECTION
{
    LEFT    = 100,
    TOP     ,
    RIGHT   = 200,
    BOTTOM  ,
};
    

enum.cpp


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

int main()
{
    std::cout << LEFT   << "\t";
    std::cout << TOP    << "\t";
    std::cout << RIGHT  << "\t";
    std::cout << BOTTOM << std::endl;
    
    return 0;
}
    

実行方法

コンパイルする時には、ヘッダーファイル(enum.h)は記述しません。


g++ enum.cpp
    

実行結果


100	101	200	201
    

const

  1. 定数は、define で定義する以外に、 変数の先頭に const キーワードをつけて定数として定義することもできます。
  2. 変数に const キーワードをつけた場合には、コンパイル時に型チェックが行われて、 プログラムのミスを検知しやすくなります。

const.cpp


#include <iostream>

#define NUM "0"

int main()
{
    if (NUM)
    {
        std::cout << NUM << " is not zero." << std::endl;
    } else {
        std::cout << NUM << " iz zero." << std::endl;
    }
    return 0;
}
    

実行結果


0 is not zero.
    

コード説明

  1. 数値の 0 を defien を使って NUM という定数にしようとした時に、間違って文字列の "0" を設定してしまったとします。
  2. 数値の 0 は false と判定されますが、文字列の "0" は true と判定されます。
  3. 結果として意図しない結果が表示されてしまいます。


そこで、const キーワードを使った定数定義に変えてみます。

const.cpp


#include <iostream>

const int NUM = "0";    // ここが変更されています。

int main()
{
    if (NUM)
    {
        std::cout << NUM << " is not zero." << std::endl;
    } else {
        std::cout << NUM << " iz zero." << std::endl;
    }
    return 0;
}
    

コード説明

  1. 変数定義の先頭に const キーワードをつけると、その変数は定数となります。
  2. この const.cpp をコンパイルしようとすると
    「char[2]をintに変換できません」という趣旨のエラーがでます。
  3. char[ ]は、C++での正式な文字列の型です。
  4. 文字列の型は複雑なのでC++では、std::string で簡易的に扱えるようにしています。


次に、エラーで指摘された点を修正します。


#include <iostream>

const int NUM = 0;  // ここが修正されています。

int main()
{
    if (NUM)
    {
        std::cout << NUM << " is not zero." << std::endl;
    } else {
        std::cout << NUM << " iz zero." << std::endl;
    }
    return 0;
}
    

実行結果


0 iz zero.
    

コード説明

  1. このように、const を使った定数定義をすると、 コンパイル時に型チェックが行われて、エラーが検知される度合いが増えます。
  2. 逆に言えば、実行時に変なことになることが少なくなるわけです。
  3. このような理由により、C++では、define による定数定義よりも、 const による定数定義が推奨されています。

const のいろいろな使い方

  1. constキーワードは、前述の「const定数」以外に「constオブジェクト」 「constメンバ関数」「const引数」「constポインター」などの使い方があります。
  2. 「constメンバ関数」は「constオブジェクト」とセットで使われます。


constオブジェクトとconstメンバ関数


#include <iostream>

class Human
{
protected:
    std::string name;
public:
    Human(std::string name) : name(name) {}
    void Iam()
    {
        std::cout << "I am " << name << "." << std::endl;
    }
    void Name(std::string name)
    {
        this->name = name;
    }
};

int main()
{
    Human man("M1");
    man.Name("Bob");
    man.Iam();
    Human *woman = new Human("F1");
    woman->Name("Mary");
    woman->Iam();
    delete woman;
    
    return 0;
}
    

実行結果


I am Bob.
I am Mary.
    


上記のプログラムの manオブジェクトとwomanオブジェクトに constキーワードをつけると、そのオブジェクトのメンバ変数は、 コンストラクタによる初期設定以外では変更できなくなります。


#include <iostream>

class Human
{
protected:
    std::string name;
public:
    Human(std::string name) : name(name) {}
    
    // メンバ関数の先頭ではなく()の後ろにconstをつけます。
    void Iam() const
    {
        std::cout << "I am " << name << "." << std::endl;
    }
    
    // メンバ変数を変更するメンバ関数にはconstをつけられません
    void Name(std::string name)
    {
        this->name = name;
    }
};

int main()
{
    const Human man("M1");
    //man.Name("Bob");      // constのついていないメンバ関数は呼び出せません
    man.Iam();
    const Human *woman = new Human("F1");
    //woman->Name("Mary");  // constのついていないメンバ関数は呼び出せません
    woman->Iam();
    delete woman;
    
    return 0;
    

実行結果


I am M1.
I am F1.
    

コード説明

  1. 一複雑そうですが、仕組みがわかれば簡単です。
  2. オブジェクト定義に const をつければ、そのオブジェクトは const オブジェクトになります。
  3. constオブジェクトからは、constメンバ関数しか呼び出せません。
  4. constメンバ関数は、メンバ関数の( )の後ろにconstをつけて定義します。
  5. メンバ変数を変更するようなメンバ関数には、constをつけられません。もしつけると コンパイル時エラーになります。


const引数


#include <iostream>

int plusOne(const int num);

int main()
{
    std::cout << plusOne(100) << std::endl;
    
    return 0;
}

int plusOne(const int num)
{
    //return ++num; これはエラーです。
    return num + 1;
}
    

実行結果


101
    

コード説明

  1. const指定した引数は、その関数の中で、値を変更することはできません。
  2. メンバ関数で使用する場合も同じです。
  3. num++はnumの値を返してから1増やしますが、 ++numは1増やしてからnumの値を返します。


constポインター


#include <iostream>

void plusOne(const int * num);

int main()
{
    int num = 100;
    plusOne(&num);
    std::cout << num << std::endl;
    
    return 0;
}

void plusOne(const int * num)
{
    //++*num;   これはエラー
    std::cout << *num + 1 << std::endl;
}
    

実行結果


101
100
    

コード説明

  1. const指定したポインター引数は、その関数の中で ポインターが指し示す値を変更することはできません。
  2. メンバ関数で使用する場合も同じです。




369 visits
Posted: Dec. 11, 2019
Update: Dec. 12, 2019

ホーム   C/C++チュートリアル   目次