Этот вопрос длинный, поэтому, пожалуйста, потерпите меня.
Я пытаюсь решить имеющуюся у меня проблему с управлением памятью, общими указателями и картами. Я просто хотел получить некоторую обратную связь о моей архитектуре, возможно, некоторые из вас сделали это лучше в прошлом.
В следующих примерах будет сделан псевдокод.
У меня есть класс слушателя:
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 или подписаться на него. подписываться Метод принимает значение ключа и необработанный указатель.
Синглтон управляет
std::map<std::string name, 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 всегда очищен последним, так что объявление необработанного указателя и передача методу подписки приведет к тому же поведению.
Есть идеи? Я что-то упустил очевидное и интуитивное? Я неправильно понимаю использование общих указателей в этом случае?
Любая помощь с благодарностью. Я куплю тебе пива.
Ваш дизайн сочетает в себе два известных шаблона дизайна, наблюдатель а также команда.
Observer определяет зависимость один-ко-многим между объектами, чтобы при изменении одного объекта
состояние, все зависимые клиенты уведомляются и обновляются автоматически.
Команда инкапсулирует запрос как объект, что позволяет вам параметризовать клиентов с помощью
различные запросы, запросы очереди или журнала, а также поддержка отменяемых операций.
Я бы рекомендовал изучить эти шаблоны (см. Ссылки выше) и реорганизовать ваш дизайн, чтобы отделить инкапсуляцию запросов от наблюдения за этими запросами.
Прежде всего, я бы порекомендовал последовать совету rhalbersma в другом ответе. Кроме того, я бы перепроверил, если вам нужен синглтон. Я не вижу острой необходимости в том, что вы описали.
Технически это проблема владения. Предоставленная вами информация требует от меня угадать несколько вещей. Я полагаю MyListener
экземпляр должен быть разделен между разными вещами, Command
объект и что-то еще (так как вы используете shared_ptr
). Таким образом, вы должны действительно разделить эту собственность:
class Command {
private:
boost::shared_ptr<MyListener> m_listener;
};
а также
CommandManager::isntance()->subscribe("TaterTot", ttl);
Таким образом, MyListener будет освобожден последним владельцем.
Благодаря авторам, мне удалось создать минимальный, компилируемый пример именно того, что я хотел, используя Observer Design Pattern, как предложено @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;
}
Я включаю полный исходный код для пользователей Google, которые могут застрять в той же проблеме. Еще раз, спасибо, StackOverflow.