良い方法があれば教えてください

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
dom

良い方法があれば教えてください

#1

投稿記事 by dom » 13年前

コード:

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();  // 余計ややこしい
} 
コードを簡潔にする何か良い方法はないでしょうか?

Poco
記事: 161
登録日時: 15年前

Re: 良い方法があれば教えてください

#2

投稿記事 by Poco » 13年前

Hogeのコンストラクタ、デストラクタでそれぞれBegin()とEnd()を呼ぶのでは駄目なのでしょうか?

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: 良い方法があれば教えてください

#3

投稿記事 by beatle » 13年前

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();
    }
}

dom

Re: 良い方法があれば教えてください

#4

投稿記事 by dom » 13年前

何種類もあります。
私はあまり設計が得意ではないので、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をメンバ変数に持つのは気持ち悪い感じもします。

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: 良い方法があれば教えてください

#5

投稿記事 by beatle » 13年前

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();
}

dom

Re: 良い方法があれば教えてください

#6

投稿記事 by dom » 13年前

最初からきちんと書かなくてすみません。
ブロックで囲む方法や、Pocoさんの意見はもっともだと思います。
自分でも整理できていなかったのであらためて書き直します。

コード:

class Hoge
{
    public:
        void Begin();
        void End();
 
        // その他メンバ関数
        void Func1();
        void Func2();
};
1.その他メンバ関数が呼ばれる前に一度Begin()が呼ばれないといけない。
2.オブジェクトが破棄される前にEnd()が呼ばれないといけない。
3.Begin(), End()はそれぞれHogeのコンストラクタ、デストラクタからは呼べない。

という場合を考えたいと思います。
今のところ、インターフェースをまとめてループで回すのが、コード修正が少ない方法かと思います。

vog

Re: 良い方法があれば教えてください

#7

投稿記事 by vog » 13年前

コード:

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;
}
テンプレートで基底クラスにするのはどうでしょう?

dom

Re: 良い方法があれば教えてください

#8

投稿記事 by dom » 13年前

継承とは思いつきそうで思いつきませんでした。
ありがとうございます。

閉鎖

“C言語何でも質問掲示板” へ戻る