ページ 1 / 1
良い方法があれば教えてください
Posted: 2012年8月28日(火) 22:02
by dom
コード:
class Hoge
{
public:
void Begin();
void End();
// その他メンバ関数
void Func1();
void Func2();
};
void main() {
Hoge a;
a.Begin();
a.Func1();
a.Func2();
a.End();
}
このような、オブジェクトの使用前にBegin()、使用後はEnd()を実行しないといけないクラスがあります。
しかし、オブジェクト数が増えるとこの作業は手間です。
なので次のようなクラスを考えました。
コード:
template <class T>
class Auto
{
public:
Auto() { T.Begin(); }
~Auto() { T.End(); }
operator T&() { return val_; }
private:
T val_;
};
// 例えば次のようにしたいがコンパイルエラー
void main() {
Auto<Hoge> a;
a.Func1(); // aをHogeのオブジェクトであるかのように扱いたい
}
// 実行可
void main() {
Auto<Hoge> a;
static_cast<Hoge>(a).Func1(); // 余計ややこしい
}
コードを簡潔にする何か良い方法はないでしょうか?
Re: 良い方法があれば教えてください
Posted: 2012年8月28日(火) 22:30
by Poco
Hogeのコンストラクタ、デストラクタでそれぞれBegin()とEnd()を呼ぶのでは駄目なのでしょうか?
Re: 良い方法があれば教えてください
Posted: 2012年8月28日(火) 22:44
by beatle
Begin, Endを呼び出す必要があるクラスが何種類もあって、以下のようになって困っているのでしょうか?
コード:
Hoge a;
Foo b;
Bar c;
// Beginを手動で何回も呼び出さないといけない・・・
a.Begin();
b.Begin();
c.Begin();
// do something
// Endも手動で呼び出さないといけない・・・
a.End();
b.End();
c.End();
これは Hoge, Foo, Bar クラスが共通のクラスを親として持てば、簡潔に書けそうですね。
コード:
class IBeginEnd
{
public:
virtual ~IBeginEnd() {}
virtual void Begin() = 0;
virtual void End() = 0;
};
class Hoge : public IBeginEnd
{
public:
void Begin();
void End();
// その他メンバ関数
void Func1();
void Func2();
};
class Foo : public IBeginEnd
{
// something
};
class Bar : public IBeginEnd
{
// something
};
int main() // void ではなく int が規格的に正しいです
{
std::vector<IBeginEnd*> instances;
Hoge a;
Foo b;
Bar c;
instances.push_back(&a);
instances.push_back(&b);
instances.push_back(&c);
for (int i = 0; i < instances.size(); ++i)
{
instances[i]->Begin();
}
// do something
for (int i = 0; i < instances.size(); ++i)
{
instances[i]->End();
}
}
Re: 良い方法があれば教えてください
Posted: 2012年8月28日(火) 23:10
by dom
何種類もあります。
私はあまり設計が得意ではないので、Hoge, Foo, Barなどが増えたり減ったりし、
そのたびにBegin、Endを書いていたので何かよい方法はないかと考えていました。
beatleさんの意見を参考にさせてもらい、このようにしてみました。
コード:
void Init(); // HogeのBegin()はこの関数の後に呼ばれないといけない。
void Exit(); // HogeのEnd()はこの関数の前に呼ばれないといけない。
class Worker
{
public:
Worker() {
instances.push_back(&a);
instances.push_back(&b);
instances.push_back(&c);
for (int i = 0; i < instances.size(); ++i)
{
instances[i]->Begin();
}
}
~Worker() {
for (int i = 0; i < instances.size(); ++i)
{
instances[i]->End();
}
}
void DoSomething();
private:
Hoge a;
Foo b;
Bar c;
std::vector<IBeginEnd*> instances;
};
void main()
{
Worker worker;
Init();
....
worker.DoSomething();
....
Exit();
}
変数の増減に伴い変更するのはinstances.push_backの部分のみになり少し楽そうです。
しかし、そのためだけにinstancesをメンバ変数に持つのは気持ち悪い感じもします。
Re: 良い方法があれば教えてください
Posted: 2012年8月28日(火) 23:32
by beatle
Poco さんの No.2 の書き込みにも返信してくださいね。
僕のやり方は Poco さんの案より優れているわけではないので、 Poco さんの案を採用できそうならそちらが良いと思います。
Init, Begin, End, Exit の順に呼ばねばならず、コンストラクタで Begin を、デストラクタで End を呼ぶと
Begin, Init, Exit, End の順になってしまうではないか、という心配をするかもしれませんが、次のようにすれば解決できます。
コード:
// Hoge, Foo, Barのコンストラクタとデストラクタで Begin, End を呼び出すようにしておきます
// したがって、Workerのコンストラクタでは暗黙的に各インスタンスの Begin が呼ばれ、
// Workerのデストラクタでは暗黙的に各インスタンスの End が呼ばれます。
int main() // int ですよ
{
Init();
{
Worker worker; // Begin が呼ばれる
worker.DoSomething();
} // End が呼ばれる
Exit();
}
Re: 良い方法があれば教えてください
Posted: 2012年8月29日(水) 00:43
by dom
最初からきちんと書かなくてすみません。
ブロックで囲む方法や、Pocoさんの意見はもっともだと思います。
自分でも整理できていなかったのであらためて書き直します。
コード:
class Hoge
{
public:
void Begin();
void End();
// その他メンバ関数
void Func1();
void Func2();
};
1.その他メンバ関数が呼ばれる前に一度Begin()が呼ばれないといけない。
2.オブジェクトが破棄される前にEnd()が呼ばれないといけない。
3.Begin(), End()はそれぞれHogeのコンストラクタ、デストラクタからは呼べない。
という場合を考えたいと思います。
今のところ、インターフェースをまとめてループで回すのが、コード修正が少ない方法かと思います。
Re: 良い方法があれば教えてください
Posted: 2012年8月29日(水) 12:45
by vog
コード:
class Hoge
{
public:
void Begin();
void End();
// その他メンバ関数
void Func1();
void Func2();
};
template<typename T>
class Auto: public T // Tを基底クラスとする
{
public:
Auto(){ T::Begin(); }
~Auto(){ T::End(); }
};
int main()
{
Auto<Hoge> a;
// 継承関係にあるので…
a.Func1();
Hoge& temp = a;
temp.Func2();
return 0;
}
テンプレートで基底クラスにするのはどうでしょう?
Re: 良い方法があれば教えてください
Posted: 2012年8月29日(水) 14:43
by dom
継承とは思いつきそうで思いつきませんでした。
ありがとうございます。