レベルアップ!

備忘録として綴ります。

C++ メンバイニシャライザの効率を調べてみた

C++にてクラスを作り、コンストラクタ内で初期化をするときに

Hoge()
{
	m_integer = 0;
	m_float = 0.0f;
	m_char = 'a';
	m_string = "abc";
};

という書き方と

Huga()
	:m_integer(0),
	m_float(0.0f),
	m_char('a'),
	m_string("abc")
{};

という書き方があります。 
Huga()の書き方ををメンバイニシャライザと言います。
コンストラクタで初期化するときはこのメンバイニシャライザの方が効率がいいとのことです。

オブジェクトがインスタンス化される際の流れは、次のような感じになっています。

1.オブジェクトをインスタンス化する。
2.クラスが持つメンバ変数のコンストラクタが起動し、中に書かれている処理が実行される。
3.コンストラクタの内容が実行される。
2の部分で、メンバ変数のコンストラクタが呼び出されていて、それぞれ初期化済みになっています。 メンバイニシャライザを使わずに代入で初期化しようとすると、それは3のタイミングになりますから、 2と3とで、処理が重複してしまいます

コンストラクタとデストラクタ | Programming Place Plus C++編【言語解説】 第13章


でも効率ってどのくらい良いんだろう?と疑問に思い、調べてみました。
以下のソースを用意し処理時間を計測してみました。

#include <iostream>
#include <string>
#include <Windows.h>
class Hoge
{
public:
        //コンストラクタ内で初期化
	Hoge()
	{
		m_integer = 0;
		m_float = 0.0f;
		m_char = 'a';
		m_string = "abc";
	};
	~Hoge() {};
private:
	int m_integer;
	float m_float;
	char m_char;
	std::string m_string;
};

class Huga
{
public:
        //メンバイニシャライザ
	Huga()
		:m_integer(0),
		m_float(0.0f),
		m_char('a'),
		m_string("abc")
	{};
	~Huga() {};
private:
	int m_integer;
	float m_float;
	char m_char;
	std::string m_string;
};

int main()
{
	const int MAX = 1000 * 1000;	//100万回
        //コンストラクタ内で初期化
	std::cout << "Hoge"<<std::endl;
	DWORD hogeStart = GetTickCount();
	for (int i = 0; i < MAX; i++)
	{
		Hoge hoge;
	}
	DWORD hogeEnd = GetTickCount();
	std::cout << "Hoge_duration: " << (double)(hogeEnd - hogeStart) / 1000 << std::endl;

        //メンバイニシャライザ
	std::cout << "Huga" << std::endl;
	DWORD hugaStart = GetTickCount();
	for (int i = 0; i < MAX; i++)
	{
		Huga hoge;
	}
	DWORD hugaEnd = GetTickCount();
        //秒で表示
	std::cout << "Huga_duration: " << (double)(hugaEnd - hugaStart) / 1000 << std::endl;
	std::getchar();
	return 0;
}

10万回ループを回してもこの程度のクラスだと差分が出なかったので、100万回回してみました。時間は秒です。
Hoge=コンストラクタ内で初期化
Huga=メンバイニシャライザ
1回目 差分0.392
f:id:jagabeeInitialize:20180121185947p:plain

2回目 差分0.171
f:id:jagabeeInitialize:20180121190028p:plain

3回目 差分0.313
f:id:jagabeeInitialize:20180121190054p:plain

4回目 差分0.188
f:id:jagabeeInitialize:20180121190724p:plain

5回目 差分0.297
f:id:jagabeeInitialize:20180121190841p:plain

だいたい0.2~0.4秒ほど早いですね。
メンバイニシャライズの方が効率がいい、というのがこれで分かりました。
記述も何となくそっちの方がかっこいいから、と言う理由でメンバイニシャライズで初期化していましたがコンストラクタの仕組みを一つ理解できた気がします。

また、上記リンク先にある通り

メンバイニシャライザでのメンバ変数の記述順については、 クラス定義の中でメンバ変数を書いた順番通りにするのが基本です。 これは、メンバ変数それぞれのコンストラクタが呼び出される順番は、 クラス定義の中でメンバ変数を書いた順番と同じだからです。 余計な混乱を招かないように、順番を合わせておきましょう。

とのことです。初期化の順番も大事なんですね。