TENKAIKEN Storehouse

Contents ♪

C++ C言語からの文法の追加 2024.08.14
デフォルト引数
関数呼び出しの末尾から省略可能な引数を定義できる。
省略時に適用するデフォルト値は、関数宣言か関数のプロトタイプ宣言の何れかで指定する。
戻り値型 関数名(引数1, 引数2, 引数3=デフォルト, 引数4=デフォルト);
例のようにプロトタイプ宣言でデフォルト設定を書いているならば、関数定義側で指定しなくてもよい。
デフォルトを設定できる引数は末尾の側であり、デフォルト引数から後ろの引数は全てデフォルト引数でなければならない。関数の呼び出しでデフォルト設定のある引数を省略した場合は、関数定義のデフォルト値が適用される。
#include <iostream>
using namespace std;

void mul(float x, int i = 10);

void mul(float x, int i)
{
    cout << x * i << endl;
}

int main()
{
    mul(3.0, 5);        // 15
    mul(2.5);           // 25

    return 0;
}
C++ C言語からの文法の追加 2024.08.14
グローバルスコープのスコープ解決
同名のグローバルスコープの変数とローカル変数をローカルスコープ内で参照するときは、ローカル変数のアクセスになるが、スコープ解決演算子「::」により変数がグローバルスコープであることを明示して参照できる。
#include <iostream>
using namespace std;

int gx = 10;

int main()
{
    int gx = 200;

    cout << gx << endl;         // 200
    cout << ::gx << endl;       // 10

    return 0;
}
C++ C言語からの文法の追加 2024.08.14
インライン関数
inlineで修飾する関数はインライン関数として、コンパイラの判断によりインライン展開される。
inline関数は呼び出し時のオーバーヘッドを削減でき、マクロ関数の代替にできる。
#include <iostream>
using namespace std;

inline int max(int a, int b)
{
    return (a > b)? a : b; 
}

int main()
{
    cout << max(7, 6) << endl;          // 7

    return 0;
}
コンパイラの判断により必ずインライン展開されるとは限らない。
C++ C言語からの文法の追加 2024.08.14
constによる定数
const宣言した変数は定数となり初期化以降に変更できなくなる。
これは#defineのマクロ定数の代わりになり、マクロ展開時の副作用を考慮しなくてもよくなる。constの変数宣言はヘッダファイルに書いて、複数からincludeされてもリンカエラーにならない。
const char*とchar* constは、constの位置で意味が異なる。
例のp1は、p1への代入が定数なのではなく、p1が指し示す文字列が定数である。p2は、p2というポインタ変数が定数であり、ポインタ参照先を初期化以降に変更できない。
関数の引数を参照渡しにする場合、その参照先を関数内で変更することを防止するには、関数の引数をconstで宣言すればよい。
#include <iostream>
using namespace std;

const int SIZE = 10;

void func(const char* p)
{
    //*p = 'A';         // Compile Error
    cout << p << endl;
}

int main()
{
    char s[SIZE];

    const char* p1 = 0; 
    p1 = s; 
    //*p1 = 'A';        // Compile Error

    char* const p2 = s; 
    //s = 0;            // Compile Error
    *p2 = 'A';

    func("ABC");

    return 0;
}
C++ C言語からの文法の追加 2024.08.14
関数のオーバーロード
引数リストの異なる同名の関数を複数定義することができる。
どの関数を呼び出すかは、呼び出し時の引数の型によって選択される。引数リストが同じで戻り値が異なる関数はオーバーロードできない。この例では、
int mul(int a, int b)
のようなオーバーロードはできない。
#include <iostream>
using namespace std;

void mul(int a, int b)
{
    cout << a * b << endl;
}

void mul(double a, double b)
{
    cout << a * b << endl;
}

void mul(double a, double b, double c)
{
    cout << a * b * c << endl;
}

int main()
{
    mul(3, 5);              // 15
    mul(2.3, 1.2);          // 2.76
    mul(1.7, 0.8, 4.5);     // 6.12

    return 0;
}
C++ C言語からの文法の追加 2024.08.14
C言語のリンケージ指定
extern "C"のブロックに宣言する関数は、Cでコンパイルされていることを示す。
C++の中でC関数を呼び出す場合は、関数プロトタイプをリンケージ指定する必要がある。
宣言する関数が1つだけの場合はブロックにせず、
extern "C" int repeat(char *s, int cnt);
のようにしてもよい。
testc.cpp
#include <stdio.h>

int repeat(char *s, int cnt)
{
    int i;
    for (i = 0; i < cnt; i++) 
        printf("%s\n", s);
    return cnt;
}
#include <iostream>
using namespace std;

extern "C" {
int repeat(char *s, int cnt);
};

int main()
{
    repeat("Jive", 3);

    return 0;
}
Jive
Jive
Jive
C++ C言語からの文法の追加 2024.08.14
エイリアスとしての参照
変数の別名として参照を宣言できる。
型& 参照 = 変数
参照は、初期化する変数と同一の実体を参照する別名である。参照はポインタのようなメモリアドレスを代入してデータの格納先を間接参照するのではなく、同じ実体に対しもう一つの別の名前の変数を持つということである。
参照の宣言には、必ず参照元の初期化を伴う。ポインタ変数のようにNULLで初期化して後で代入するようなことはできない。初期化には以下の機会がある。
変数宣言
関数呼び出し時
関数が参照を返す時
コンストラクタでの初期化
変数宣言での参照は、別名になるための他の変数で初期化する必要がある。クラスメンバの参照は、コンストラクタで初期化する必要がある。関数引数の参照では、関数呼び出しのたびに参照の初期化が行われる。例のra、rbとfunc()のxは全て一つの変数aそのものである。
#include <iostream>
using namespace std;

int& func(int& x)
{
    x *= 10;
    return x;
}

int main()
{
    int a = 5;
    int& ra = a;

    ra = 10;
    cout << a << endl;      // 10

    int& rb = func(ra);
    cout << a << endl;      // 100
    a++;
    cout << rb << endl;     // 101

    return 0;
}
C++ C言語からの文法の追加 2024.08.14
参照を返す関数
参照を戻り値に返す関数はreturnで戻り値の参照を初期化する。
参照を返す関数を呼び出して返るとき、その関数は戻り値の別名となるので、例の場合のrefgx()への代入は、グローバル変数gxへの代入となる。refgx()の部分をgxに置き換えたことと同じである。
#include <iostream>
using namespace std;

int gx;

int& refgx()
{
    return gx;
}

int main()
{
    int x = refgx() = 100;
    cout << x << endl;          // 100
    cout << gx << endl;         // 100
    cout << ++refgx() << endl;  // 101

    return 0;
}
C++ C言語からの文法の追加 2024.08.14
変数の初期化方法
変数の初期化の記述方法は例のa、b、c、dのような4種類があり、どれを使ってもかまわない。
初期化と代入は厳密には同じではないが「=」代入演算子は代入と変数の初期化の両方で使われる。この曖昧さを避ける方法として、=を伴わない{}の初期化の書き方がC++11から導入され、初期化について数値型、配列、構造体、クラスを同じ書式で書けるようになった。
#include <iostream>
using namespace std;

struct data {
    int     x;
    double  d;
};

int main()
{
    int a = 10;
    int b(10);
    int c = {10};
    int d{10};

    int ar2[] = {10, 20, 30};
    int ar1[]{10, 20, 30};

    struct data sd1 = {5, 2.5};
    struct data sd2{5, 2.5};

    return 0;
}
C++ C言語からの文法の追加 2024.08.14
名前空間
namespace構文により、ブロック内を任意の名前空間に設定できる。
namespace 名前空間名;
namespaceのブロック内の変数や関数のアクセスは、スコープ解決演算子で名前空間を指定する必要がある。usingにより名前空間名を宣言したスコープ内では、その名前空間名のスコープ解決を省略できる。
using namespace 名前空間名;
#include <iostream>

namespace spaceA
{
    static int ga = 100;
    void func()
    {
        std::cout << "spaceA func()" << std::endl;
    }
}

namespace spaceB
{
    static int gb = 200;
    void func()
    {
        std::cout << "spaceB func()" << std::endl;
    }
}

int main()
{
    std::cout << spaceA::ga << std::endl;
    spaceA::func();
    std::cout << spaceB::gb << std::endl;
    spaceB::func();

    {
        using namespace spaceA;
        std::cout << ga << std::endl;
        func();
    }
    {
        using namespace spaceB;
        std::cout << gb << std::endl;
        func();
    }
    return 0;
}
100
spaceA func()
200
spaceB func()
100
spaceA func()
200
spaceB func()
名前空間は複数をネストすることができる。
C++ C言語からの文法の追加 2024.08.14
bool(真理値)型
bool型は真理値を持つ型である。
bool型のリテラルはtrue、falseで、整数にするとtrueは1でfalseは0に変換される。真理値の偽は0であり、0以外は真である。比較演算の結果はbool型で取得できる。
#include <iostream>
using namespace std;

int main()
{
    int a = 5;
    bool k = (a < 10);

    if (k == false)
        cout << (int)false << endl;
    else
        cout << (int)true << endl;

    return 0;
}
C++ C言語からの文法の追加 2024.08.14
標準出力std::cout
std::coutは、<<演算子により与えられる文字列や数値、変数を標準出力へ出力(表示)する。
std::coutを使うにはiostreamヘッダをincludeする必要がある。usingで名前空間をstdに指定することでstd::を省略できる。
coutへ与える数値型やstd::stringは、内部的に<<演算子のオーバーロードoperator<<()で適切に文字列で出力される。改行はエスケープ文字の\nでもよいが、std::endlで改行できる。数値はstd::hexを通すことで16進数表記に変換できる。
#include <iostream>
using namespace std;

int main()
{
    int a = 1234;
    cout << "a=" << a << 89 << '\n';        // a=123489
    cout << 2.45 << endl;                   // 2.45 
    cout << hex << 4096 << endl;            // 1000 

    return 0;
}
C++ C言語からの文法の追加 2024.08.14
標準出力std::coutの書式
iomanipヘッダには、std::coutなどと共に使用する書式を整えるための関数(マニピュレータ)が定義されている。
setw(桁) 表示桁数
left、right フィールド内で左寄せ・右寄せ
setfill(文字) フィールド内の空白を埋める文字
setprecision(桁) 浮動小数点の有効桁数
#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
    cout << 1234 << endl;
    cout << setw(8) << 1234 << endl;
    cout << setw(8) << left << 1234 << endl;
    cout << setw(8) << right << 1234 << endl;
    cout << setw(8) << right << setfill('.') << 1234 << endl;
    cout << setprecision(4) << 123.4567 << endl;

    return 0;
}
1234
    1234
1234
    1234
....1234
123.5
C++ C言語からの文法の追加 2024.08.14
標準入力std::cin
std::cinは、標準入力からの入力を>>演算子によりを変数へ格納する。
std::cinは改行で入力終了とみなされる。
std::cin.fail()がtrueの場合は、直前の入力が失敗したことを示す。例の最初の整数型のnumへ入力するところで、数値ではない文字を入力してみると「input error」が表示される。
setw()で入力桁数を制限できる。例ではsetw(桁)にbufのバッファサイズの5を指定している。これは終端0を含めた桁数なので、実際には-1少ない4桁数で切り詰められる。
#include <iostream>
#include <iomanip>
#include <cstdio>
using namespace std;

int main()
{
    int num;
    cout << "num=";
    cin >> num;
    if (cin.fail())
        cout << "input error"; 
    cout << num << endl;

    char buf[5];
    cin >> setw(5) >> buf;
    cout << "buf=" << buf << endl;

    return 0;
}
num=1234
1234
ABCD
buf=ABCD