内存管理的想法,std :: map,boost :: shared_ptr

时间:2013-01-29 14:46:24

标签: c++ design-patterns shared-ptr observer-pattern command-pattern

这个问题很长,所以请耐心等待。

我正试图解决我在内存管理,共享指针和地图方面遇到的两难问题。我只是希望得到一些关于我的架构的反馈,也许有些人过去会做得更好。

以下示例将是伪代码。

我有一个听众课程:

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

每次调用对象更新时都会引发这种情况。我正在使用一个名为OpenDDS的中间件来进行进程间通信框架。

我有一个Command类,它继承了DDS对象,并利用了on_data_received()。当引发on_data_received()时,我想从上面的类中调用handleUpdate()方法。

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

这就是问题所在。管理它的类是Singleton,并使用两个方法发布订阅来发布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

}

取消分配后,boost会删​​除它对共享指针的所有权。 CommandManager尝试通过删除名为“TaterTot”的所有对象来“清理”,但由于boost :: shared_ptr已经自行清理,因此会引发双重内存损坏。 CommandManager单例最后始终清理,因此声明原始指针并传递给subscribe方法将导致相同的行为。

有什么想法吗?我错过了一些明显而直观的东西吗?我是否误解了此实例中共享指针的用法?

非常感谢任何帮助。我会买啤酒。

3 个答案:

答案 0 :(得分:2)

您的设计混合了两种众所周知的设计模式,ObserverCommand

Observer定义对象之间的一对多依赖关系,以便在一个对象发生更改时 state,所有依赖客户端都会自动得到通知和更新。

Command将请求封装为对象,从而允许您使用参数化客户端 不同的请求,队列或日志请求,并支持可撤销的操作。

我建议研究这些模式(参见上面的链接)并重构你的设计,将请求的封装与观察这些请求分开。

答案 1 :(得分:0)

首先,我建议在另一个答案中遵循rhalbersma的建议。另外,我会仔细检查你是否需要一个单身人士。我没有看到你所描述的对它的强烈需求。

从技术上讲,这是一个所有权问题。您提供的信息要求我猜几件事。我想MyListener实例应该在不同的东西之间共享,Command对象和其他东西(因为你使用shared_ptr)。所以你需要真正分享这个所有权:

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

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

因此MyListener将被最后一位所有者解除分配。

答案 2 :(得分:-1)

感谢贡献者,我设法使用@rhalbersma建议的Observer设计模式创建了一个最小的,可编辑的示例。

#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。