コンテンツにスキップ

コンストラクタ

オブジェクトを作成する際に呼び出されるメンバ関数をコンストラクタといいます。 コンストラクタはデータメンバの初期化を行うために使用します。

クラス名と同じ名前で戻り値がない関数がコンストラクタになります。

class Rectangle {
 public:
    Rectangle(int height, int width);

    int Area() const;

 private:
    const int height_;
    const int width_;
};

このコンストラクタを使ってオブジェクトを作成するには次のようにします。

Rectangle r(10, 20);

データメンバの初期化

コンストラクタでデータメンバを初期化するには次のようにします。

class Rectangle {
 public:
    Rectangle(int height, int width) : height_(height), width_(width) {}

    int Area() const {
        return height_ * width_;
    }

 private:
    const int height_;
    const int width_;
};

初期化は値の変更ではないため、 コンストラクタに渡された値から const データメンバの値を設定することができます。

クラス宣言とは別に定義

クラス宣言とは別にコンストラクタを定義するには次のようにします。

class Rectangle {
 public:
    Rectangle(int height, int width);

    int Area() const {
        return height_ * width_;
    }

 private:
    const int height_;
    const int width_;
};

Rectangle::Rectangle(int height, int width) : height_(height), width_(width) {}

デフォルトコンストラクタ

値を1つも受け取らないコンストラクタをデフォルトコンストラクタといいます。

class Rectangle {
 public:
    Rectangle() : height_(0), width_(0) {}

    int Area() const {
        return height_ * width_;
    }

 private:
    const int height_;
    const int width_;
};

デフォルトコンストラクタを使ってオブジェクトを作成するには次のようにします。

Rectangle r;

明示的に定義するコンストラクタが1つもない場合のみ、 コンパイラが暗黙的にデフォルトコンストラクタを定義します。

コピーコンストラクタ

そのクラスの参照だけを受け取るコンストラクタをコピーコンストラクタといいます。

class Copyable {
 public:
    Copyable();  // デフォルトコンストラクタ
    Copyable(const Copyable& c);  // コピーコンストラクタ
};

コピーコンストラクタを使ってオブジェクトを作成するには次のようにします。

Copyable c1;  // デフォルトコンストラクタでオブジェクト作成
Copyable c2(c1);  // コピーコンストラクタでオブジェクト作成

const ではない参照であってもコピーコンストラクタになりますが、 コピー元を変更せずにコピーを行うために const の参照にすることが多いです。

コピーコンストラクタを定義していないクラスでは、 コンパイラによって暗黙的にコピーコンストラクタが定義されます。

暗黙的にコピーコンストラクタが定義されないケース

コピーコンストラクタを定義していないクラスであっても、 特定の条件を満たした場合には暗黙的なコピーコンストラクタの定義は行われなくなります。

条件の一例として次のものがあります。

  • コピーできないデータメンバをもつ
  • 右辺値参照型のデータメンバをもつ
  • 下記のいずれかが明示的に定義されている
    • ムーブコンストラクタ
    • ムーブ代入演算子

詳細は コピーコンストラクタ - cppreference.com を参照してください。

ムーブコンストラクタ

そのクラスの右辺値参照だけを受け取るコンストラクタをムーブコンストラクタといいます。

class Movable {
 public:
    Movable();  // デフォルトコンストラクタ
    Movable(Movable&& m);  // ムーブコンストラクタ
};

ムーブコンストラクタを使ってオブジェクトを作成するには次のようにします。

Movable m1;  // デフォルトコンストラクタでオブジェクト作成
Movable m2(std::move(m1));  // ムーブコンストラクタでオブジェクト作成

ムーブコンストラクタを定義していないクラスでは、 コンパイラによって暗黙的にムーブコンストラクタが定義されます。

暗黙的にムーブコンストラクタが定義されないケース

ムーブコンストラクタを定義していないクラスであっても、 特定の条件を満たした場合には暗黙的なムーブコンストラクタの定義は行われなくなります。

条件の一例として次のものがあります。

  • ムーブできないデータメンバをもつ
  • 下記のいずれかが明示的に定義されている
    • コピーコンストラクタ
    • コピー代入演算子
    • ムーブ代入演算子
    • デストラクタ

詳細は ムーブコンストラクタ - cppreference.com を参照してください。

変換コンストラクタと explicit

値を1つだけ受け取るコンストラクタを変換コンストラクタといいます。 コピーコンストラクタやムーブコンストラクタも変換コンストラクタです。

class Square {
 public:
    Square(int size) : size_(size) {}

    int Area() const {
        return size_ * size_;
    }

 private:
    int size_;
};

受け取る値が2個の場合と同様にオブジェクトを作成するには次のようにします。

Square s(10);

以下のようにした場合、 int から Square への暗黙的な型変換で変換コンストラクタが使用されます。

Square s = 10;

暗黙的な型変換で使用されないようにするには 変換コンストラクタに explicit をつけます。

暗黙的な型変換を意図して使用する場合を除き、 受け取る値が1つのコンストラクタには explicit をつけることが望ましいです。

コピーコンストラクタとムーブコンストラクタ

コピーコンストラクタとムーブコンストラクタを explicit にすると 関数の戻り値で値渡しすることができなくなります。 一般的にコピーコンストラクタとムーブコンストラクタは explicit にはしません。

class Square {
 public:
    explicit Square(int size) : size_(size) {}

    int Area() const {
        return size_ * size_;
    }

 private:
    int size_;
};

explicit をつけると以下の記述はコンパイルエラーになります。

Square s = 10;

継承

派生クラスのコンストラクタは 基底クラスのデフォルトコンストラクタを暗黙的に呼び出します。

constructor_inheritance.cc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

class Rectangle {
 public:
    Rectangle() : height_(0), width_(0) {
        std::cout << "Rectangle::Rectangle() is called." << std::endl;
    }

    int Area() const {
        return height_ * width_;
    }

 private:
    const int height_;
    const int width_;
};

class Square : public Rectangle {
 public:
    Square() {
        std::cout << "Square::Square() is called." << std::endl;
    }
};

int main() {
    Square s;  // Square クラスのデフォルトコンストラクタを使用
    std::cout << "area = " << s.Area() << std::endl;
    return 0;
}

実行結果は以下のようになります。

Rectangle::Rectangle() is called.
Square::Square() is called.
area = 0

コンストラクタの実行順序は、必ず次の順序になります。

  1. 基底クラスのコンストラクタ
  2. 派生クラスのコンストラクタ

基底クラスのコンストラクタを明示的に呼び出すこともできます。

class Rectangle {
 public:
    Rectangle(int height, int width) : height_(height), width_(width) {}

    int Area() const {
        return height_ * width_;
    }

 private:
    const int height_;
    const int width_;
};

class Square : public Rectangle {
 public:
    Square(int size) : Rectangle(size, size) {}
};