閉じる

【C++】変数の初期化の種類と書き方まとめ

C++では、変数の初期化方法がとても多く、どれを使えばよいか迷いやすい言語です。

本記事では、代表的な初期化の種類と書き方を、図解とサンプルコードを交えながら整理します。

初心者の方はもちろん、「とりあえず=で代入している」という方も、これを機に初期化スタイルを整理して理解してみてください。

【C++】変数の初期化の種類と書き方まとめ

変数の初期化とは何か

変数の初期化とは、変数を宣言すると同時に、最初の値を与えることを指します。

C++では、単にint x;と宣言した場合、ローカル変数なら中身は不定であり、そのまま使うと未定義動作につながります。

そのため、通常は次のように「宣言+初期化」をセットで書くことが基本です。

C++
int x = 10;  // 宣言と同時に10で初期化

C++には、以下のように複数の初期化スタイルがあります。

  • コピー初期化
  • 直接初期化
  • リスト初期化(統一初期化)
  • 値初期化
  • デフォルト初期化

以降では、それぞれの意味と書き方、違いを順番に解説します。

コピー初期化(copy initialization)

基本の書き方と特徴

コピー初期化は= を使う最も馴染みのある書き方です。

C++
int a = 10;       // 整数のコピー初期化
double pi = 3.14; // 実数のコピー初期化
std::string s = "hello"; // 文字列のコピー初期化

見た目は「代入」のようですが、宣言と同時に書いた=初期化です。

コンストラクタ呼び出しが必要な型でも、コンパイラが内部的にコンストラクタを呼び出してくれます。

クラス型での挙動

C++
#include <iostream>
#include <string>

class Person {
public:
    std::string name;

    // コンストラクタ
    Person(const std::string& n) : name(n) {
        std::cout << "Person(const std::string&) が呼ばれました\n";
    }
};

int main() {
    Person p = "Taro";  // コピー初期化
    std::cout << "name = " << p.name << std::endl;
}
実行結果
Person(const std::string&) が呼ばれました
name = Taro

このように、Person p = "Taro";も、コンストラクタを使った初期化として扱われます。

直接初期化(direct initialization)

括弧を使う初期化

直接初期化は、括弧()を使ってコンストラクタや変換を直接呼び出すスタイルです。

C++
int a(10);          // 整数の直接初期化
double pi(3.14);    // 実数の直接初期化
std::string s("hi"); // 文字列の直接初期化

クラス型で使うと、コンストラクタ呼び出しがより明確になります。

C++
#include <iostream>
#include <string>

class Person {
public:
    std::string name;

    Person(const std::string& n) : name(n) {
        std::cout << "コンストラクタ呼び出し\n";
    }
};

int main() {
    Person p("Taro");  // 直接初期化
    std::cout << "name = " << p.name << std::endl;
}
実行結果
コンストラクタ呼び出し
name = Taro

コピー初期化と直接初期化は、多くの場合同じように動作しますが、テンプレートや明示的コンストラクタなどの場面で振る舞いが異なることがあります。

基本的にはクラスの生成には直接初期化を好んで使うスタイルもよく見られます。

リスト初期化(統一初期化) – { } を使う方法

なぜリスト初期化が重要なのか

C++11以降では、{ } を使ったリスト初期化が導入されました。

「統一初期化」とも呼ばれ、さまざまな型に対して共通の初期化構文として使えるのが大きな特徴です。

C++
int a{10};          // 整数のリスト初期化
double pi{3.14};    // 実数のリスト初期化
std::string s{"hi"}; // 文字列のリスト初期化

配列やコンテナ、構造体の初期化でも自然に使えます。

C++
#include <vector>
#include <string>

int main() {
    int arr[3]{1, 2, 3};          // 配列
    std::vector<int> v{1, 2, 3};  // vector
    std::string s{"hello"};       // string
}

ナローイング変換を防ぐメリット

リスト初期化の大きな利点は、不用意な型変換(ナローイング)をコンパイル時にエラーにできることです。

C++
int main() {
    int x1 = 3.14;  // OK (小数部分が切り捨てられる)
    int x2{3.14};   // エラー(ナローイング変換が禁止される)
}

「誤って情報を失うような代入」を防ぎたいときには、{ } で初期化すると安全性が上がります。

値初期化(value initialization)

値初期化の書き方と意味

値初期化は、変数を「既定の値」で初期化する方法です。

プリミティブ型では0、クラス型ではデフォルトコンストラクタが呼ばれます。

代表的な書き方は次の2つです。

  • T var{};
  • T var(); (※関数宣言と紛らわしいため通常は避ける)
C++
#include <iostream>

class Foo {
public:
    Foo() {
        std::cout << "Foo のデフォルトコンストラクタ\n";
    }
};

int main() {
    int x{};  // 0で初期化
    double d{}; // 0.0で初期化
    Foo f{}; // デフォルトコンストラクタ呼び出し

    std::cout << "x = " << x << ", d = " << d << std::endl;
}
実行結果
Foo のデフォルトコンストラクタ
x = 0, d = 0

「とりあえず0クリアしたい」「未初期化を絶対に避けたい」という場合に、T var{};の書き方は非常に便利です。

デフォルト初期化(default initialization)

デフォルト初期化とは

デフォルト初期化とは、初期化式を書かずに変数を宣言したときの挙動です。

C++
int x;   // デフォルト初期化(ローカル変数なら中身は不定)
Foo obj; // クラスならデフォルトコンストラクタが呼ばれる

注意すべき点は、組み込み型(プリミティブ型)のローカル変数は値が不定ということです。

一方で、グローバル変数や静的変数は、暗黙に0初期化されます。

C++
#include <iostream>

int gx; // グローバル変数(0で初期化される)

int main() {
    int lx; // ローカル変数(不定値)

    std::cout << "gx = " << gx << std::endl;
    std::cout << "lx = " << lx << std::endl; // 未定義動作の可能性
}
実行結果
gx = 0
lx = (環境によって予測不能な値)  ← 実際には出力結果は保証されない

このようなバグを防ぐために、ローカル変数には必ず明示的な初期化を書きましょう。

おすすめはint x{};int x = 0;のように、意図をはっきりさせる書き方です。

初期化スタイルの書き分け指針

よく使うパターンの整理表

よくある場面ごとに、どの初期化スタイルを選ぶかを整理します。

用途・状況推奨スタイル例コード
ローカルの整数・実数など値初期化 or リスト初期化int x{};
クラスのインスタンス生成直接初期化 or リスト初期化Foo f(10);
配列やstd::vectorリスト初期化std::vector<int> v{1,2,3};
ナローイングを防ぎたい数値リスト初期化int x{value};

実例: いろいろな初期化をまとめて見る

C++
#include <iostream>
#include <vector>
#include <string>

class Point {
public:
    int x;
    int y;

    // コンストラクタ
    Point(int x_, int y_) : x(x_), y(y_) {}
};

int main() {
    // 1. 組み込み型
    int a{};       // 値初期化(0)
    int b{10};     // リスト初期化
    int c = 20;    // コピー初期化

    // 2. クラス型
    std::string s1("hello");   // 直接初期化
    std::string s2 = "world";  // コピー初期化
    std::string s3{"C++"};     // リスト初期化

    // 3. 配列とコンテナ
    int arr[3]{1, 2, 3};               // 配列のリスト初期化
    std::vector<int> v{1, 2, 3, 4, 5}; // vectorのリスト初期化

    // 4. 構造体・クラス
    Point p1(1, 2);    // 直接初期化
    Point p2{3, 4};    // リスト初期化

    std::cout << "a = " << a << ", b = " << b << ", c = " << c << "\n";
    std::cout << "s1 = " << s1 << ", s2 = " << s2 << ", s3 = " << s3 << "\n";
    std::cout << "p1(" << p1.x << "," << p1.y << "), p2(" << p2.x << "," << p2.y << ")\n";
}
実行結果
a = 0, b = 10, c = 20
s1 = hello, s2 = world, s3 = C++
p1(1,2), p2(3,4)

このように、同じ「初期化」でも構文の違いでニュアンスが変わることを、コード全体で確認しておくと理解が深まります。

まとめ

C++の変数初期化には、コピー初期化、直接初期化、リスト初期化、値初期化、デフォルト初期化といった複数のスタイルが存在します。

実務では{} を使ったリスト初期化・値初期化を基本にし、クラスには括弧による直接初期化を併用すると、安全で意図が明確なコードになりやすいです。

特にローカル変数の未初期化は大きなバグ要因になるため、「宣言したら必ず初期化を書く」という習慣を身につけておくと安心です。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!