#c #instantiation #simulator
#c #создание экземпляра #тренажёр
Вопрос:
Я нахожусь в ситуации, когда я считаю, что две реализации верны, и я не знаю, какую из них выбрать.
У меня есть приложение, имитирующее считыватели карт. Он имеет графический интерфейс, в котором вы выбираете, какой последовательный порт и скорость использовать, а также кнопку воспроизведения и остановки.
Я ищу лучшую реализацию для построения reader.
У меня есть SimulatorCore
класс, который живет до тех пор, пока мое приложение SimulatorCore создает экземпляр Reader
класса. И можно будет имитировать несколько считывателей на нескольких последовательных портах.
Две возможности:
-
My
Reader
— это указатель (динамическое создание экземпляра), я создаю его при нажатии кнопки воспроизведения, удаляю его при нажатии кнопки остановки. -
Мой
Reader
объект (статическое создание экземпляра), я создаю его вSimulatorCore
конструкторе, затем создаю и вызываюReader.init()
иReader.cleanup()
в свой класс Reader и вызываю их при воспроизведении и остановке
Я лично вижу функциональную сторону, и я явно хочу использовать указатель, и у меня нет экземпляра reader, если не моделируется reader.
Кто-то говорит мне, что я должен использовать статическое создание экземпляра (причина: для безопасности и потому, что «плохо использовать указатель, когда у вас есть выбор не использовать их»)
Я не знаком с ними, но я думаю, что я также могу использовать smart pointer.
Примеры кода: 1-е решение:
class SimulatorCore
{
play(){reader = new Reader();};
stop(){delete reader; reader = nullptr;};
private:
Reader *reader;
}
Примеры кода: 2-е решение:
class SimulatorCore
{
play(){reader.init();};
stop(){reader.cleanup();};
private:
Reader reader;
}
Код не протестирован, я просто использовал его для иллюстрации.
Каково наилучшее решение? Почему?
Ответ №1:
Вы можете легко использовать shared_ptr / unique_ptr:
class SimulatorCore
{
play(){_reader = make_shared<Reader>();};
stop(){_reader = nullptr};
private:
shared_ptr<Reader> _reader;
}
Я думаю, это правильно решит вашу проблему.
Динамическое распределение создает некоторые проблемы, например, с выбросом исключения (например, может произойти потеря памяти, если между play() и stop() возникнет исключение, и stop() никогда не будет вызван). Или вы можете просто забыть где-нибудь вызвать stop() перед уничтожением SimulatorCore, это возможно, если программа тяжелая.
Если вы никогда не пробовали умные указатели, это хороший шанс начать это делать.
Комментарии:
1. Спасибо, я наконец-то использую unique_ptr, и он отлично работает. Я приму другой ответ из-за объяснений, но вы решили мою проблему.
Ответ №2:
Обычно вам следует избегать выполнения динамического выделения new
самостоятельно, поэтому, если вы собираетесь использовать 1-е решение, вам следует вместо этого использовать интеллектуальные указатели.
Однако главный вопрос здесь — это вопрос логики. Реальное устройство чтения карт существует в состоянии ожидания, пока оно не используется. Во 2-м решении, что делать init
и cleanup
делать? Они просто переводят устройство чтения карт в состояние ожидания или начинают имитировать фактическое считывание карты? Если это первый случай, я предполагаю, что такое поведение должно быть в конструкторе и деструкторе Reader
, а затем создание Reader
объекта означает создание устройства чтения карт. Если это второй случай, то я бы сказал, что 2-е решение в значительной степени правильное, просто функции плохо названы.
Мне кажется наиболее логичным что-то вроде этого:
class SimulatorCore
{
play(){reader.start();};
stop(){reader.stop();};
private:
Reader reader;
}
Да, все, что я сделал, это изменил имена функций Reader
. Однако функции теперь не несут ответственности за инициализацию или очистку считывателя — эта ответственность находится в руках Reader
конструктора и деструктора. Вместо start
этого и stop
начните и завершите моделирование Reader
. Затем один Reader
экземпляр может входить и выходить из этого режима моделирования несколько раз за время своего существования.
Если позже вы захотите распространить эту идею на несколько Reader
s, вы можете просто изменить элемент на:
std::vector<Reader> readers;
Однако я не могу точно знать, что это то, что вы хотите, потому что я не знаю логики вашей программы. Надеюсь, это даст вам несколько идей.
Опять же, что бы вы ни решили сделать, вам следует избегать использования new
для выделения ваших Reader
s, а затем также избегать использования необработанных указателей для ссылки на эти Reader
s. Используйте интеллектуальные указатели и соответствующие make_...
им функции для динамического выделения этих объектов.
Комментарии:
1. Действительно хорошее объяснение, спасибо. Я буду использовать smart pointer, я думаю, что это лучшее решение, и мне нужно их практиковать! Кнопка остановки фактически останавливает симуляцию, нет симуляции = нет считывателя, поэтому я думаю, что хорошо удалить считыватель.
2. @MoKaT Хорошо, если это ваша логика, тогда это звучит нормально!
Ответ №3:
Это явно зависит от того, как организована вся ваша программа, но в целом, я думаю, я бы предпочел статический подход из соображений ответственности:
Предположим, у вас есть отдельный класс, который обрабатывает последовательную связь. Этот класс будет отправлять и получать сообщения и отправлять их в класс reader. Сообщение может прийти в любое время. Разница между динамическим и статическим подходами заключается в:
- При динамическом подходе последовательный класс должен проверить, действительно ли существует средство чтения, прежде чем отправлять сообщение. Или читатель должен зарегистрироваться и отменить регистрацию в последовательном классе.
- При статическом подходе класс reader может сам решить, способен ли он обработать сообщение в данный момент или нет.
Поэтому я думаю, что статический подход немного проще и понятнее.
Однако, если есть вероятность, что в будущем вам придется внедрять другие, разные классы reader, динамический подход упростит это расширение, поскольку соответствующий класс можно легко создать во время выполнения.
Таким образом, динамический подход обеспечивает большую гибкость.
Комментарии:
1. Спасибо, мое общение является частью моего устройства чтения (поскольку мне, возможно, понадобится несколько устройств чтения на разных последовательных портах — USB). И у меня есть несколько классов чтения, но на другом уровне (где у меня есть абстрактный базовый класс, который является моим интерфейсом для создания экземпляров разных читателей). Интеллектуальный указатель кажется идеальным для этого случая.
2. Я упростил свой вопрос, но
Reader
мы говорим здесь о конечном автомате, который создает экземпляр реального reader. Но это был скорее функциональный вопрос, чем технический.3. Хорошо, в первой части этого ответа я попытался показать пример, в котором «всегда присутствующее и действительное» свойство статических объектов является преимуществом. Основываясь на том, что вы добавили сюда, я думаю, я бы тоже использовал интеллектуальные указатели.