1

この質問は長いので、ご容赦ください。

私は、メモリ管理、共有ポインター、およびマップに関するジレンマを解決しようとしています。私は私のアーキテクチャについてフィードバックを得たかっただけです。

次の例は、擬似コードで行われます。

私はリスナークラスを持っています:

class MyListener {
   friend class Command;
   public:
      MyListener() {}
      virtual ~MyListener() {}
      void handleUpdate() {
           std::cout << "Update Handled" << std::endl;
      }
};

これは、オブジェクトの更新が呼び出されるたびに呼び出されます。プロセス間通信フレームワークに OpenDDS というミドルウェアを使用しています。

DDS オブジェクトを継承し、on_data_received() を利用する Command クラスがあります。on_data_received() が呼び出されたら、上記のクラスから handleUpdate() メソッドを呼び出したいと思います。

class Command {
   public:
      /*standard constructor destructor here*/
      void on_data_received() {
          m_listener->handleUpdate();
      }
      void write();
   private:
      MyListener *m_listener;
 };

ここに問題があります。これを管理するクラスはシングルトンであり、publishsubscribeの 2 つのメソッドを使用して、DDS メッセージを発行するか、DDS メッセージを購読します。subscribeメソッドは、キー値と生のポインターを受け取ります。

シングルトンは、

std::map<std::string name, Command>

CommandクラスにはMyListenerクラスが含まれています。

これを破る疑似コードのスニペットを次に示します。

class TaterTotListener : public MyListener {
     void handleCommand() {
         std::cout << "Tater tot found" << std::endl;
     }
};

int main() {
    // make a new smart pointer to the listener
    boost::shared_ptr<TaterTotListener> ttl(new TaterTotListener);
    // tell the singleton we want to publish an object called "TaterTot"
    CommandManager::instance()->publish("TaterTot");
    // tell the singleton we want to subscribe to an object called tater tot
    CommandManager::isntance()->subscribe("TaterTot", ttl.get());

    // processing goes here
    // deallocation

}

割り当てが解除されると、ブーストは共有ポインターの所有権を削除します。CommandManager は「TaterTot」という名前のすべてのオブジェクトを削除して「クリーンアップ」を試みますが、boost::shared_ptr はすでに自身をクリーンアップしているため、二重の空きメモリ破損がスローされます。CommandManager シングルトンは常に最後にクリーンアップされるため、生のポインターを宣言して subscribe メソッドに渡すと、同じ動作になります。

そこに何かアイデアはありますか?明白で直感的な何かを見逃していませんか? この場合の共有ポインタの使用法を誤解していますか?

どんな助けでも大歓迎です。ビールを買います。

4

3 に答える 3

2

あなたの設計では、 ObserverCommandという 2 つのよく知られた設計パターンが混在しています。

Observer は、オブジェクト間の 1 対多の依存関係を定義するため、1 つのオブジェクトの状態が変化すると、それに依存するすべてのクライアントに通知され、自動的に更新されます。

Command はリクエストをオブジェクトとしてカプセル化するため、クライアントをさまざまなリクエストでパラメータ化したり、リクエストをキューまたはログに記録したり、取り消し可能な操作をサポートしたりできます。

これらのパターンを調査し (上記のリンクを参照)、設計をリファクタリングして、リクエストのカプセル化をそれらのリクエストの観察から分離することをお勧めします。

于 2013-01-29T15:21:20.637 に答える
0

まず第一に、別の回答でrhalbersmaのアドバイスに従うことをお勧めします。また、シングルトンが必要かどうかを再確認します。あなたが説明したことから、それの強い必要性はわかりません。

技術的には所有権の問題です。あなたが提供した情報から、いくつかのことを推測する必要があります。インスタンスは、オブジェクトと他の何かMyListenerの間で共有する必要があると思います( を使用しているため)。したがって、この所有権を次のように共有する必要があります。Commandshared_ptr

class Command {
   private:
      boost::shared_ptr<MyListener> m_listener;
};

CommandManager::isntance()->subscribe("TaterTot", ttl);

そのため、MyListener は最後の所有者によって割り当て解除されます。

于 2013-01-29T18:22:10.477 に答える
-1

寄稿者のおかげで、@rhalbersma によって提案された Observer Design Pattern を使用して、まさに私が望んでいたことの最小限のコンパイル可能な例を作成することができました。

#include <cstdlib>
#include <iostream>
#include <map>
#include <boost/shared_ptr.hpp>
#include <vector>

class Listener {
public:
    virtual void handleUpdate() = 0;
    std::string *string;
};

class Command {
    std::vector<boost::shared_ptr<Listener> > listener;

public:

    ~Command() {
    }

    void setListener(boost::shared_ptr<Listener> l) {
        listener.push_back(l);
    }

    void handleCommand() {
        for (int i = 0; i < listener.size(); i++) {
            //std::cout << "Handle command " << i << std::endl;
            listener.at(i)->string = string;
            listener.at(i)->handleUpdate();
        }
    }

    void write(std::string value) {
        std::cout << "Write 1" << std::endl;
        string = &value;
        handleCommand();
    }

private:
    std::string *string;
};

class MyListener : public Listener {
public:

    MyListener(int num) : num(num) {
    };

    ~MyListener() {
    }

    void handleUpdate() {
        std::cout << "Listener " << num << " with string " << *string << std::endl;
    }

private:

    int num;
};

class CommandManager {
public:

    void publish(std::string name, std::string value) {
        std::map<std::string, boost::shared_ptr<Command> >::iterator it;
        it = commandMap.find(name);
        if (it == commandMap.end()) {
            // add a new one
            boost::shared_ptr<Command> command(new Command());
            commandMap[name] = command;
        }

        it = commandMap.find(name);
        it->second->write(value);
    }

    void subscribe(std::string name, boost::shared_ptr<Listener> l) {
        std::map<std::string, boost::shared_ptr<Command> >::iterator it;
        it = commandMap.find(name);
        if (it == commandMap.end()) {
            boost::shared_ptr<Command> command(new Command());
            command->setListener(l);
            commandMap[name] = command;
        } else {

            it->second->setListener(l);
        }
    }        

private:

    std::map<std::string, boost::shared_ptr<Command> > commandMap;
};

int main(int argc, char** argv) {

    boost::shared_ptr<MyListener> myListener0(new MyListener(0));
    boost::shared_ptr<MyListener> myListener1(new MyListener(1));
    boost::shared_ptr<MyListener> myListener2(new MyListener(2));

    CommandManager commandManager;

    commandManager.subscribe("Tyler", myListener0);
    commandManager.subscribe("Tyler", myListener1);
    commandManager.subscribe("Tyler", myListener2);

    commandManager.publish("Tyler", " is cool");

    return 0;
}

同じ問題で立ち往生している可能性のある Google ユーザー向けに、完全なソースを含めます。繰り返しますが、StackOverflow に感謝します。

于 2013-01-29T19:46:37.650 に答える