这个问题很长,所以请耐心等待。
我正试图解决我在内存管理,共享指针和地图方面遇到的两难问题。我只是希望得到一些关于我的架构的反馈,也许有些人过去会做得更好。
以下示例将是伪代码。
我有一个听众课程:
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方法将导致相同的行为。
有什么想法吗?我错过了一些明显而直观的东西吗?我是否误解了此实例中共享指针的用法?
非常感谢任何帮助。我会买啤酒。
答案 0 :(得分:2)
您的设计混合了两种众所周知的设计模式,Observer和Command。
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。