Функциональные против безопасности / статические против динамических экземпляров

Я нахожусь в ситуации, когда я думаю, что две реализации являются основными, и я не знаю, какую из них выбрать.

У меня есть приложение, имитирующее устройства чтения карт. Он имеет графический интерфейс, в котором вы можете выбрать, какой последовательный порт и скорость использовать, а также кнопку воспроизведения и остановки.

Я ищу лучшую реализацию для построения читателей.

у меня есть SimulatorCore класс, который живет так долго, как мое заявление
SimulatorCore инстанцирует Reader учебный класс. И можно будет смоделировать несколько считывателей на нескольких последовательных портах.

Две возможности:

  • мой Reader является указателем (динамическое создание), я создаю его при нажатии кнопки воспроизведения, удаляю при нажатии кнопки остановки.

  • мой Reader это объект (статическое воплощение), я воплощаю его в SimulatorCore конструктор затем создать и вызвать Reader.init() а также Reader.cleanup() в мой класс Reader и вызывать их, когда ударили play и stop

Я лично вижу функциональную сторону, и я явно хочу использовать указатель, и у меня нет экземпляра какого-либо читателя, если он не имитируется.

Кто-то говорит мне, что я должен использовать статическое создание экземпляров (Причина: для безопасности, и потому что «плохо использовать указатель, когда у вас есть выбор не использовать их»)

Я не знаком с ними, но я думаю, что я также могу использовать умный указатель.

Примеры кода: 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;
}

Код не тестируется, я приведу его в качестве иллюстрации.

Какое лучшее решение? Зачем ?

0

Решение

Как правило, следует избегать динамического выделения с new сами, поэтому, если вы собираетесь использовать 1-е решение, вы должны вместо этого использовать умные указатели.

Однако главный вопрос здесь — это вопрос логики. Реальный кард-ридер существует в режиме ожидания, пока он не используется. Во втором решении, что делать init а также cleanup делать? Они просто переводят устройство чтения карт в состояние ожидания или начинают имитировать фактическое считывание карты? Если это первый случай, я предлагаю, чтобы это поведение было в конструкторе и деструкторе Reader, а затем создание Reader Объект означает создание кард-ридера. Если это второй случай, то я бы сказал, что 2-е решение в значительной степени правильно, просто что функции имеют неправильные имена.

Что кажется мне наиболее логичным, так это что-то вроде этого:

class SimulatorCore
{
play(){reader.start();};
stop(){reader.stop();};

private:
Reader reader;
}

Да, все, что я сделал, это изменил имена функций для Reader, Однако функции теперь не несут ответственности за инициализацию или очистку считывателя — эта ответственность находится в руках Readerконструктор и деструктор. Вместо, start а также stop начало и конец симуляции Reader, Один Reader Затем экземпляр может входить и выходить из этого режима симуляции несколько раз за время своего существования.

Если позже вы захотите распространить эту идею на несколько Readers, вы можете просто изменить члена на:

std::vector<Reader> readers;

Тем не менее, я не могу знать наверняка, что это то, что вы хотите, потому что я не знаю логику вашей программы. Надеюсь, это даст вам некоторые идеи.

Опять же, что бы вы ни решили сделать, вам следует избегать использования new выделить свой Readers, а затем также избегать использования сырых указателей для ссылки на эти Readers. Используйте умные указатели и их соответствующие make_... функции для динамического размещения этих объектов.

2

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

Вы можете легко использовать shared_ptr / unique_ptr:

class SimulatorCore
{
play(){_reader = make_shared<Reader>();};
stop(){_reader = nullptr};

private:
shared_ptr<Reader> _reader;
}

Я думаю, это решит вашу проблему правильно.

Динамическое выделение создает некоторые проблемы, например, с исключением исключений (может произойти потеря памяти, если между play () и stop (), например, возникнет исключение, и stop () никогда не будет вызываться). Или вы можете просто забыть где-нибудь вызвать stop () до уничтожения SimulatorCore, это возможно, если программа тяжелая.

Если вы никогда не пробовали умные указатели, это хороший шанс начать это делать.

3

Это явно зависит от того, как организована вся ваша программа, но в целом, я думаю, я бы предпочел статический подход, потому что ОТВЕТСТВЕННОСТЬ соображения:

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

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

Поэтому я думаю, что статический подход немного проще и понятнее.

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

Таким образом, динамический подход предлагает больше гибкости.

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