トップページ – Modern C++編 C++編](../../index.html) – 第7章
問題① 次のようにコンストラクタを定義することには問題があります。指摘してください。
class Student {
public:
Student(const char* name, int grade, int score);
Student(const char* name, int score, int grade);
};
コンストラクタを複数定義できますが、実引数によって区別が付くものでなければなりません。このプログラムの場合、引数の並びが「const char*、int、int」のようにまったく同じになっており、引数の名前にしか違いがなく、区別を付けられませんから、コンパイルエラーになります。
問題② メンバイニシャライザを使った初期化と、コンストラクタ内で代入によって初期値を設定する方法とで、パフォーマンスにどの程度の違いがあるか、計測してください(パフォーマンス測定マクロが、コードライブラリにあります)
適当なクラス型のメンバ変数📘を持ったクラスを2つ用意して、片方はメンバイニシャライザを使い、他方はコンストラクタ内で代入するようにします。
#include "ppp_perform.h"
class Rect {
public:
Rect() = default;
Rect(double left, double right, double top, double bottom) :
mLeft(left), mRight(right), mTop(top), mBottom(bottom)
{}
void Set(double left, double right, double top, double bottom)
{
mLeft = left;
mRight = right;
mTop = top;
mBottom = bottom;
}
private:
double mLeft;
double mRight;
double mTop;
double mBottom;
};
class Test1 {
Rect mR1;
Rect mR2;
Rect mR3;
public:
Test1();
};
Test1::Test1()
{
mR1.Set(10.0, 10.0, 30.5, 30.5);
mR2.Set(15.5, 10.0, 52.5, 52.5);
mR3.Set(15.5, 15.5, 52.5, 52.5);
}
class Test2 {
Rect mR1;
Rect mR2;
Rect mR3;
public:
Test2();
};
Test2::Test2() :
mR1(10.0, 10.0, 30.5, 30.5),
mR2(15.5, 10.0, 52.5, 52.5),
mR3(15.5, 15.5, 52.5, 52.5)
{
}
int main()
{
PPP_CHECK_PERFORM_BEGIN(10000000);
Test1 t1;
PPP_CHECK_PERFORM_END("don't use member_initializer");
PPP_CHECK_PERFORM_BEGIN(10000000);
Test2 t2;
PPP_CHECK_PERFORM_END("use member_initializer");
}実行結果:
don't use member_initializer: 0.847000 seconds
use member_initializer: 0.812000 seconds
わずかですが、メンバイニシャライザを使う方が高速になることが分かります。メンバ変数の個数がもっと多かったり、もっと複雑な型であったり、インスタンス化📘するオブジェクトの数が多かったりすると、さらにこの差が大きくなっていきますから、可能である限り、つねにメンバイニシャライザを使うようにしてください。
問題③ int型の変数の値を退避(保存)させておき、最後に確実に元の値を復元することをサポートするようなクラスを設計してください。つまり、次のような挙動になるようにしてください (X がクラスとします)。
int value = 10;
// この関数を呼び出したときに value が 10 なら、
// 抜け出した後も確実に 10 であるようにしたい。
void func(bool flag)
{
X store(/* 引数は任意 */);
value = 50;
if (flag) {
return;
}
value = 100;
}
このクラスは、コンストラクタで int型の変数を指定すれば、その時点での変数の値を保存しておき、デストラクタでその値へ確実に復元してくれます。ここでは、ValueStore という名前のクラスにしてみます。
// ValueStore.h
#ifndef VALUE_STORE_H_INCLUDED
#define VALUE_STORE_H_INCLUDED
// int型の変数の値を退避し、破棄時に復元する
class ValueStore {
public:
ValueStore(int* ptr) :
mPtr(ptr), mSaveValue(*ptr)
{
}
~ValueStore()
{
*mPtr = mSaveValue;
}
private:
int* const mPtr; // 対象の変数のメモリアドレス
const int mSaveValue; // 元の値
};
#endif// main.cpp
#include <iostream>
#include "ValueStore.h"
namespace {
int value = 10;
// この関数を呼び出したときに value が 10 なら、
// 抜け出した後も確実に 10 であるようにしたい。
void func(bool flag)
{
ValueStore store(&value);
value = 50;
if (flag) {
return;
}
value = 100;
}
}
int main()
{
func(true);
std::cout << value << std::endl;
func(false);
std::cout << value << std::endl;
}実行結果:
10
10
デストラクタを利用することによって、func関数内をどのような形で抜け出すとしても気にすることなく、確実に復元処理が行われます。この例でいえば、途中で return する経路もありますが、ここを通っても通らなくても問題ありません。
このように、デストラクタは必ずしも、解放📘やファイルクローズといった、後片付けのような処理にしか使えないわけではありません。「確実に行ってほしいこと」を書くことができる、非常に役に立つ機能です。
Programming Place Plus のトップページへ
| はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
| X で ポスト/フォロー | LINE で送る | noteで書く |
|
|
管理者情報 | プライバシーポリシー |