Идеи для управления памятью, std :: map, boost :: shared_ptr

Этот вопрос длинный, поэтому, пожалуйста, потерпите меня.

Я пытаюсь решить имеющуюся у меня проблему с управлением памятью, общими указателями и картами. Я просто хотел получить некоторую обратную связь о моей архитектуре, возможно, некоторые из вас сделали это лучше в прошлом.

В следующих примерах будет сделан псевдокод.

У меня есть класс слушателя:

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 всегда очищен последним, так что объявление необработанного указателя и передача методу подписки приведет к тому же поведению.

Есть идеи? Я что-то упустил очевидное и интуитивное? Я неправильно понимаю использование общих указателей в этом случае?

Любая помощь с благодарностью. Я куплю тебе пива.

1

Решение

Ваш дизайн сочетает в себе два известных шаблона дизайна, наблюдатель а также команда.

Observer определяет зависимость один-ко-многим между объектами, чтобы при изменении одного объекта
состояние, все зависимые клиенты уведомляются и обновляются автоматически.

Команда инкапсулирует запрос как объект, что позволяет вам параметризовать клиентов с помощью
различные запросы, запросы очереди или журнала, а также поддержка отменяемых операций.

Я бы рекомендовал изучить эти шаблоны (см. Ссылки выше) и реорганизовать ваш дизайн, чтобы отделить инкапсуляцию запросов от наблюдения за этими запросами.

2

Другие решения

Прежде всего, я бы порекомендовал последовать совету rhalbersma в другом ответе. Кроме того, я бы перепроверил, если вам нужен синглтон. Я не вижу острой необходимости в том, что вы описали.

Технически это проблема владения. Предоставленная вами информация требует от меня угадать несколько вещей. Я полагаю MyListener экземпляр должен быть разделен между разными вещами, Command объект и что-то еще (так как вы используете shared_ptr). Таким образом, вы должны действительно разделить эту собственность:

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

а также

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

Таким образом, MyListener будет освобожден последним владельцем.

0

Благодаря авторам, мне удалось создать минимальный, компилируемый пример именно того, что я хотел, используя 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.

-1
По вопросам рекламы [email protected]