1

这个问题很长,请多多包涵。

我正在尝试解决我在内存管理、共享指针和映射方面遇到的困境。我只是想得到一些关于我的架构的反馈,也许你们中的一些人过去会做得更好。

下面的例子会做的是伪代码。

我有一个监听器类:

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

每次调用对象更新时都会调用它。我正在使用一个名为 OpenDDS 的中间件来实现进程间通信框架。

我有一个命令类,它继承了一个 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 方法将导致相同的行为。

有什么想法吗?我错过了一些明显和直观的东西吗?我是否误解了在这种情况下共享指针的使用?

任何帮助是极大的赞赏。我给你买啤酒。

4

3 回答 3

2

您的设计混合了两种著名的设计模式ObserverCommand

Observer 定义了对象之间的一对多依赖关系,这样当一个对象改变状态时,它的所有依赖客户端都会被通知并自动更新。

Command 将请求封装为对象,从而使您可以对具有不同请求、队列或日志请求的客户端进行参数化,并支持可撤消的操作。

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

于 2013-01-29T15:21:20.637 回答
0

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

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

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

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

所以 MyListener 将被最后一个所有者释放。

于 2013-01-29T18:22:10.477 回答
-1

感谢贡献者,我已经成功地创建了一个最小的、可编译的示例,这正是我想要的,正如@rhalbersma 所建议的那样,使用观察者设计模式。

#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;
}

我为可能遇到同样问题的谷歌用户提供了完整的资源。再次感谢 StackOverflow。

于 2013-01-29T19:46:37.650 回答