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