C言語の機能の強化 | Programming Place Plus Modern C++編【言語解説】 第3章

トップページModern C++編

Modern C++編は作りかけで、更新が停止しています。代わりに、C++14 をベースにして、その他の方針についても見直しを行った、新C++編を作成しています。
Modern C++編は削除される予定です。

この章の概要

この章の概要です。


nullptr

C++ には、ヌルポインタを表すリテラルである nullptr が追加されています。 nullptr は、すべてのポインタ型のヌルポインタを表現できます。

nullptr は、従来の NULLマクロや 0 を使う方法と違い、「ヌルポインタを表現するリテラル」以外の解釈があり得ないため、より安全に利用できます。C++ では、ヌルポインタを表現する際には常に nullptr を使うようにするべきです。

#include <iostream>

int main()
{
    // どんなポインタ型に対してでも使用できる
    int* p1 = nullptr;
    void* p2 = nullptr;
    const void* p3 = nullptr;
    char** p4 = nullptr;

    if (p1 == nullptr) {  // あるいは !p1
        std::cout << "null" << std::endl;
    }
}

実行結果:

null

nullptr は、整数型などの他の型へは暗黙的に変換されることはありませんが、後述する bool型へは変換されます。

bool

bool型は、論理値を扱う型です。真を表すリテラルは true、偽を表すリテラルは false です。この2つ以外の値を持つことはできません。

bool b1 = true;
bool b2 = false;

bool型は分類上、整数型の一種です。そのため、汎整数拡張(C言語編第21章)の対象に含まれており、int型へ変換されます。false は 0 に、true は 1 に変換されます。

#include <iostream>

int main()
{
    int n = 100;

    n = false;
    std::cout << n << std::endl;

    n = true;
    std::cout << n << std::endl;
}

実行結果:

0
1

整数型、浮動小数点型、列挙型、ポインタ型の値は、bool型へ暗黙的に変換できます。元の値が 0 やヌルポインタであるときは false に変換され、それ以外のときは true に変換されます。

#include <iostream>

int main()
{
    enum E { e1, e2 };

    bool b1 = 0;
    bool b2 = 1;
    bool b3 = 0.0;
    bool b4 = 0.1;
    bool b5 = e1;
    bool b6 = e2;
    bool b7 = &b1;
    bool b8 = &b2;
    bool b9 = nullptr;

    std::cout << b1 << "\n"
              << b2 << "\n"
              << b3 << "\n"
              << b4 << "\n"
              << b5 << "\n"
              << b6 << "\n"
              << b7 << "\n"
              << b8 << "\n"
              << b9 << std::endl;
}

実行結果:

0
1
0
1
0
1
1
1
0

実行結果は 0 か 1 で出力されてしまっていますが、必要があれば、std::boolalphaマニピュレータを使えば、true や false という文字列で出力できます。

#include <iostream>

int main()
{
    enum E { e1, e2 };

    bool b1 = 0;
    bool b2 = 1;
    bool b3 = 0.0;
    bool b4 = 0.1;
    bool b5 = e1;
    bool b6 = e2;
    bool b7 = &b1;
    bool b8 = &b2;
    bool b9 = nullptr;

    std::cout << std::boolalpha
              << b1 << "\n"
              << b2 << "\n"
              << b3 << "\n"
              << b4 << "\n"
              << b5 << "\n"
              << b6 << "\n"
              << b7 << "\n"
              << b8 << "\n"
              << b9 << std::endl;
}

実行結果:

false
true
false
true
false
true
true
true
false

std::noboolalphaマニピュレータを使うと元の挙動、つまり論理値を整数値で出力する動作に戻ります。

!演算子は、オペランドを bool型に変換し、それを否定して返す演算子であるといえます。

#include <iostream>

int main()
{
    int n = 0;

    bool b1 = false;
    bool b2 = !b1;
    bool b3 = !n;

    std::cout << std::boolalpha
              << b1 << "\n"
              << b2 << "\n"
              << b3 << std::endl;
}

実行結果:

false
true
true

long long

C++ には、long型以上の大きさを持つ整数型 long long が追加されています。必ずしも long型よりも大きいという保証はなく、long型「以上」であることに注意してください。最低でも 64ビットの大きさを持つことは保証されています。 C言語でも、C99 から追加されています(C言語編第19章)。

他の整数型と同様に、signed や unsigned を付加できます。また、long long型の整数リテラルを表現するためには、サフィックス「ll」または「LL」を付加します。

#include <iostream>

int main()
{
    long long x1 = -1000000000LL;
    unsigned long long x2 = 1000000000ULL;

    std::cout << x1 << "\n"
              << x2 << std::endl;
}

実行結果:

-1000000000
1000000000

変数宣言位置の自由化

C++ では、変数を宣言できる箇所が柔軟になっており、(C95までの)C言語のように、ブロックの先頭でなければならないというルールは撤廃されています。

C99 でもこのルールは撤廃されています。

#include <iostream>
#include <cstring>

int main()
{
    const char* str = "abcde";
    std::cout << str << std::endl;

    std::size_t len = std::strlen(str);
    std::cout << len << std::endl;
}

実行結果:

abcde
5

変数len は、ブロック(この場合、main関数)の先頭にありませんが、C++ では許されます。

また、次のように for文の初期設定式の中で宣言することも可能です。

#include <iostream>

int main()
{
    for (int i = 0; i < 5; ++i) {
        std::cout << i << std::endl;
    }
}

実行結果:

0
1
2
3
4

この場合、変数 i のスコープは for文の内側だけに限定されます。C++ では、これは一般的な書き方です。

if文でも同様のことが可能です。

#include <iostream>

int func()
{
    return 10;
}

int main()
{
    if (int a = func()) {
        std::cout << a << " is true" << std::endl;
    }
    else {
        std::cout << a << " is false" << std::endl;
    }
}

実行結果:

10 is true

C++ では、変数を定義している部分を実行するときに、C言語よりも多くの処理が行われることがあります。そのため、無駄な処理を実行しないようにするため、変数は実際に必要になるタイミングの直前で定義するようにすべきです

ここで実行される処理はコンストラクタと呼ばれるものです。第7章で取り上げます。

範囲for文

C++ の for文は拡張され、配列などの要素がまとまったデータ構造を走査しやすくなっています。 この新たな機能は、範囲for文と呼ばれます。

#include <iostream>

int main()
{
    const int array[] = {0, 1, 2, 3, 4};

    for (int v : array) {
        std::cout << v << std::endl;
    }
}

実行結果:

0
1
2
3
4

for の後の ( ) の中に、「:」で区切った2つの項を記述します。2つ目の項に配列などのデータ構造を置き、1つ目の項には要素を受け取る変数の宣言を置きます。1回の繰り返しを行うたびに、データ構造から要素を1つ取り出し、変数に格納します。

【上級】範囲for文は、配列だけでなく、std::vector(【標準ライブラリ】第6章)のような各種コンテナに対して使えます。結局のところ、std::begin関数、std::end関数(【標準ライブラリ】第17章)が適応できる対象であれば良いです。

【上級】範囲for文で要素の値を変更したい場合は、要素の受け取りに使う変数を参照(第18章)にします(array を非const にする必要はあります)。たとえば、for (int& v : array) のように書けば、v = 0; のように代入できます。

for文であることに変わりはないので、break文や continue文も機能します。

#include <iostream>

int main()
{
    const int array[] = {0, 1, 2, 3, 4};

    for (int v : array) {
        if (v == 1) {
            continue;
        }
        if (v == 3) {
            break;
        }

        std::cout << v << std::endl;
    }
}

実行結果:

0
2

可変個引数マクロ

C++ では、通常の関数と同様に、関数形式マクロの引数も可変個にできます。 (C言語でも C99 からはできるようになりました。C言語編第52章)。

#include <cstdio>

#define PRINT_TO_STDERR

#ifdef PRINT_TO_STDERR
#define PRINT(...)  std::fprintf(stderr, __VA_ARGS__)
#else
#define PRINT(...)  std::printf(__VA_ARGS__)
#endif

int main()
{
    PRINT("%d\n", 123);
    PRINT("%s %d\n", "abc", 123);
}

実行結果:

123
abc 123

可変個引数の部分は「…」で表現します。可変でない引数があれば、通常の関数形式マクロと同様に引数名を並べてから、末尾に「…」を置きます。

置換後の並びについては、__VA_ARGS__ と書いた部分が、可変個引数の部分に指定した部分と対応します。

__func__

C++ では、現在の関数名に置換される事前定義識別子 __func__ が追加されています。 (C言語でも C99 で追加されました。C言語編第29章)。

#include <iostream>

void func()
{
    std::cout << __func__ << std::endl;
}

int main()
{
    std::cout << __func__ << std::endl;

    func();
}

実行結果:

func
main

__func__ がどのようなフォーマットの文字列になるかは環境依存です。



練習問題

問題① bool型や、各種整数型の大きさを調べるプログラムを作成して、自分が使っているコンパイラでの大きさを確認してください。

問題② 指定した文字列と、呼び出し位置の関数名を出力するログ出力マクロを作成してください。

問題③ 次のプログラムの実行結果を答えてください。

#include <iostream>

int main()
{
    int array[] = {0, 1, 2, 3, 4};

    for (int v : array) {
        v *= v;
    }

    for (int v : array) {
        std::cout << v << std::endl;
    }
}


解答ページはこちら

参考リンク


更新履歴

’2018/8/14 「bool」の項の内容を、C++編の記述に合わせるように修正。

’2018/7/10 C++編の更新に合わせて、修正を加えた。

’2018/4/2 「VisualC++」という表現を「VisualStudio」に統一。

’2018/2/22 「サイズ」という表記について表現を統一。 型のサイズ(バイト数)を表しているところは「大きさ」、要素数を表しているところは「要素数」。

≪さらに古い更新履歴を展開する≫



前の章へ (第2章 C言語との差異)

次の章へ (第4章 名前空間)

Modern C++編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る