Какова правильная парадигма создания списка указателей? - PullRequest
0 голосов
/ 16 января 2019

Я нахожу себя так часто бездумно пишу классы, которые добавляют указатели на объекты, которые впоследствии уничтожаются в списках, что приводит к множеству сбоев, но проблема в том, что иногда я просто могу ' придумает лучшее решение, чем хранить список указателей. Я думаю, что в C ++ не существует простого способа «просто сохранить временные объекты, которые вы не можете скопировать». Есть ли парадигма, по которой я скучаю, как сделать это эффективно?

Например, у меня будет класс, который выглядит примерно так:

class MyClass
public:
   std::vector<OtherClass *> other_objects;
   void method() {
      OtherObject other_object = create_other_object();
      other_objects.push_back(&other_object);
   }

Одна из причин, по которой вы можете захотеть это сделать, заключается в том, что вы используете other_objects в другом месте (в качестве аргументов для других функций) и не можете копировать OtherObject экземпляры (что может произойти, если конструктор OtherObject был закрытым например).

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

Я пишу много кода на Python и очень часто использую такую ​​структуру , когда метод заполняет итерируемый объектами, которые существуют исключительно в области действия метода. Есть ли способ сделать это эффективно в C ++? Могу ли я сделать свое собственное управление памятью, чтобы сохранить временные объекты в методе живыми вне области метода?

Ответы [ 2 ]

0 голосов
/ 16 января 2019

Какова правильная парадигма создания списка указателей в C ++?

Объедините std::list (или, возможно, какой-то другой контейнер ) с некоторым умным указателем класса (см. здесь ), например std::shared_ptr или std::unique_ptr.

Как правило (иногда неправильно) избегайте необработанных указателей и предпочитайте умные указатели. Управление памятью это сложная тема. Если вы прочтете какую-нибудь книгу по сборке мусора (например, GC handbook ), вы изучите соответствующие концепции, терминологию и методы (и они применимы даже при ручном управлении памятью , например подсчет ссылок - который некоторые люди рассматривают как примитивную форму GC). Циркулярные ссылки больно обрабатывать (помните слабые ссылки , например, std::weak_ptr).

Потребовалось бы лот пространства и времени, чтобы объяснить все это подробно (а у меня нет времени, мотивации или даже всех навыков для этого) здесь. Прочитайте хорошую книгу C ++ по программированию и обратитесь к справочнику C ++ (а позже, возможно, к C ++ 11 стандарту n3337 или к чему-то еще новее). Имейте в виду, что модель памяти из C ++ трудна для понимания (для всех).

Помните о правиле пяти .

В некоторых операционных системах и / или реализациях C ++ вы можете найти такие инструменты, как valgrind или адресное дезинфицирующее средство из Clang или GCC , который должен помочь вам отладить, а затем избежать утечек памяти (подробности зависят от компилятора и / или ОС). И в некоторых случаях (вероятно, в меньшинстве) вы можете рассмотреть возможность использования библиотеки сборщика мусора (например, Boehm's GC , см. this или MPS ).

Есть ли способ сделать это эффективно в C ++?

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


PS. C ++ - чрезвычайно сложный язык программирования . Подготовьте себя к тому, чтобы тратить много времени (годы, а не месяцы; возможно, десятилетия) и усилия на его изучение. Для вдохновения посмотрите также на исходный код некоторого хорошо написанного свободного программного обеспечения , написанного на C ++. Я не претендую на звание мастера C ++ (даже если моя ежедневная работа заключается в разработке и реализации статического исходного кода анализаторы для него). Я считаю, что на этой планете может быть всего несколько десятков (или, возможно, несколько сотен) мастеров C ++, и я не среди них.

PPS. Я пристрастен, но я рекомендую использовать Linux на вашей машине для разработки, чтобы изучать программирование на C ++, именно потому, что в нем много полезных инструментов.

0 голосов
/ 16 января 2019

Нет, нет никаких причин, по которым вы когда-либо делали бы это:

  OtherObject other_object = create_other_object();
  other_objects.push_back(&other_object);

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

В этом главное отличие Python. В Python каждый объект является ссылкой на объект Python, даже целые числа. В C ++ у вас есть объект в стеке (как у вас) или в куче (созданный с помощью new, make_unique ...). Если вы хотите имитировать поведение Python, вам нужны объекты в куче.

Существует только три допустимых шаблона, в зависимости от того, что делает create_object():

  • он создает объект и возвращает его по значению. В этом случае оберните его make_unique, чтобы получить копию. Этот шаблон обычно не очень полезен и не практичен.
  • создает новый объект в куче и ожидает, что вызывающий код будет обрабатывать управление памятью. Таким образом, прототип: std::unique_ptr<OtherClass> create_other_object();
  • он создает новый объект, но не ожидает, что вызывающий код будет обрабатывать управление памятью, в этом случае прототип должен быть OtherClass* create_other_object();

В вашем случае, это, вероятно, второй случай:

std::vector<std::unique_ptr<OtherClass>> other_objects;
void method() {
    other_objects.push_back(create_other_object());
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...