библиотеки / подходы для реализации объектных моделей в C ++ - PullRequest
2 голосов
/ 29 апреля 2009

Извиняюсь, если об этом уже спрашивали, я не совсем уверен в терминологии или в том, как задать вопрос.

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

HTML объектная модель документа (DOM) - один из примеров; в качестве другого (надуманного) примера, предположим, у меня есть эти классы:

  • Entity
  • Person (подкласс Entity)
  • Couple (подкласс Entity)
  • Property
  • House (подкласс Property)
  • Pet (подкласс Property)
  • Car (подкласс Property)

и эти отношения:

  • Entity
    • имеет 1 home класса House
    • имеет 0 или более pets класса Pet
    • имеет 0 или более cars класса Car
    • имеет 0 или более children класса Person
  • Person
    • имеет 0 или 1 spouse класса Person
    • имеет 0 или 1 marriage класса Couple
    • имеет 0 или 1 parents класса Entity (в этой модели родители не существуют, если они не живы!)
  • Couple
    • имеет 2 members класса Person
  • Property
    • имеет 1 owner класса Entity

Теперь, когда я продумал эти объекты и их отношения, я хочу начать создавать структуры данных, а также методы и поля для их обработки, и вот где я теряюсь, поскольку мне приходится иметь дело с распределением памяти и управлением временем жизни и все эти вещи. Вы можете столкнуться с такими проблемами, как: Я мог бы захотеть поместить объект в std::map или std::vector, но если я это сделаю, я не смогу хранить указатели на эти объекты, так как они могут быть перемещены, когда карта или вектор растет или уменьшается.

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

Есть ли другие подходы? Или есть библиотеки, облегчающие подобные вещи в C ++?

edit: тогда у вас возникнут другие проблемы, например, во многих случаях отношения между объектами могут быть изменяемыми, и вам нужно заранее подумать о том, как хранить ссылки на объекты и какие методы должны быть предусмотрены для доступа к объектам друг от друга. Например, если у меня есть дескриптор Person X, и я хочу представить концепцию «найти ребенка Х по имени Джордж», то я должен сохранить имя «Джордж», а не номер ребенка: дети могут хранится в векторе, и я могу вызывать X.getChildCount () и X.getChild (0), но «Джордж» не всегда может быть дочерним номером 0, поскольку другие дочерние элементы могут быть вставлены перед «Джорджем» в детский вектор. Или у Х может быть двое, трое или четверо других детей, которых также зовут Джордж. Или «Джордж» может изменить свое имя на «Энтони» или «Джорджина». Во всех этих случаях, вероятно, лучше использовать какой-то уникальный неизменяемый идентификатор.

edit 2: (и я немного проясню свой вопрос, как только уладю это) Я могу иметь дело с выбором методов и имен свойств, я могу решить, использовать ли карта или список или вектор. Это довольно просто. Вот проблемы, с которыми я пытаюсь разобраться:

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

Ответы [ 5 ]

2 голосов
/ 29 апреля 2009

Вы писали о хранении объектов из вашей объектной модели внутри std :: vector и т. Д. И проблемах с использованием указателей на них. Это напоминает мне, что хорошо разделить ваши классы C ++ на две категории (я не уверен насчет терминологии здесь):

  1. Классы сущностей , которые представляют объекты, являющиеся частью вашей объектной модели. Они обычно полиморфны или потенциально будут в будущем. Они создаются в куче и на них всегда ссылаются указатели или умные указатели. Вы никогда не создаете их непосредственно в стеке, как члены класса / структуры, и не помещаете их непосредственно в контейнеры, такие как std :: vectors. У них нет ни конструктора копирования, ни оператора = (вы можете сделать новую копию с помощью некоторого метода Clone). Вы можете сравнить их (их состояния), если это имеет значение для вас, но они не являются взаимозаменяемыми, потому что они идентичны. Каждые два объекта различны.

  2. Классы значений , которые реализуют примитивные определяемые пользователем типы (например, строки, комплексные числа, большие числа, интеллектуальные указатели, оболочки-обработчики и т. Д.). Они создаются непосредственно в стеке или как члены класса / структуры. Они копируются с помощью конструктора копирования и оператора =. Они не полиморфны (полиморфизм и оператор = не работают вместе). Вы часто помещаете их копии в контейнеры stl. Вы редко храните указатели на них в независимых местах. Они взаимозаменяемы. Когда два экземпляра имеют одинаковое значение, вы можете рассматривать их как одинаковые. (Хотя переменные, которые их содержат, разные вещи.)

Есть много очень веских причин нарушать правила. Но я заметил, что игнорирование их с самого начала приводит к тому, что программы не читаются, ненадежны (особенно когда речь идет об управлении памятью) и их трудно поддерживать.


Теперь вернемся к вашему вопросу.

Если вы хотите хранить модель данных со сложными отношениями и простым способом выполнения запросов, таких как «найти ребенка X по имени Джордж», почему бы не рассмотреть некоторую реляционную базу данных в памяти?

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

То же самое касается "коллекций всего" и идентификаторов объектов. Вам нужно будет отслеживать отношения между объектами, чтобы избежать идентификаторов без объектов в любом случае. Чем он отличается от указателей? Другой, чем получать значимые ошибки вместо того, чтобы сходить с ума по всей памяти, то есть; -)


Некоторые идеи по управлению памятью:

  • Строгое владение: когда вы можете объявить, что какой-то объект живет только столько времени, сколько его владелец, и нет возможности независимо существовать на него указатели, вы можете просто удалить его в деструкторе владельца (или с помощью scoped_ptr).

  • Кто-то уже предложил smart_ptr. Они великолепны и могут использоваться с контейнерами stl. Тем не менее, они основаны на справочной основе, поэтому не создают циклы :-(. Я не знаю каких-либо широко используемых автоматических указателей c ++, которые могут обрабатывать циклы.

  • Возможно, есть какой-то объект верхнего уровня, который владеет всеми другими объектами. Например. часто вы можете сказать, что все части принадлежат документу, алгоритму или транзакции. Они могут быть созданы в контексте объекта верхнего уровня, а затем автоматически удалены при удалении их объекта верхнего уровня (когда вы удаляете документ из памяти или заканчиваете выполнение алгоритма). Конечно, вы не можете делить части между объектами верхнего уровня.

1 голос
/ 29 апреля 2009

Существует около миллиона (по скромным подсчетам) подходов к этому. Вы действительно спрашиваете, «как мне проектировать программное обеспечение на C ++». И ответ, я боюсь, "что будет делать ваша программа?" - просто зная, что вы хотите иметь дело с людьми и домами, недостаточно.

0 голосов
/ 29 апреля 2009

Вы можете использовать boost :: shared_ptr для решения проблем с памятью. Затем вы можете свободно копировать shared_ptr, возвращать его из функций, использовать в качестве локальной переменной и т. Д.

A Person может иметь std::map< string, boost::shared_ptr<Person> >, поэтому X.getChild("George") просто будет искать дочернего элемента на карте и будет возвращать указатель. Я думаю, вы поняли концепцию, поэтому я оставлю вам остальное как упражнение;)

0 голосов
/ 29 апреля 2009

Джейсон, мой любимый источник - C ++ FAQ . Проблема в том, что вы спрашиваете: «Как я могу использовать C ++ для объектно-ориентированного программирования?»

Лучшее, что я могу сказать в SO-ответе, это:

все эти вещи будут классами в C ++, а отношения и т. Д. Будут очень похожи на языки, на которых вы привыкли собирать мусор: если вам нужны отношения между человеком и его ребенком по имени "george", вы выбираете структура данных, в которой могут храниться люди или дети, проиндексированные по имени.

Управление памятью на самом деле проще, чем в обычном C, если вы следуете некоторым правилам: убедитесь, что все объекты, которые в них нуждаются, имеют деструкторы, убедитесь, что деструкторы убирают все, что принадлежит объекту, а затем убедитесь, что вы всегда ставите динамически создаваемые объекты в ситуациях, когда они выходят за рамки, когда они больше не нужны. Это не охватит все случаи, но избавит вас, вероятно, от 80 процентов ошибок при распределении памяти.

0 голосов
/ 29 апреля 2009

Разве это не весь смысл ООП? То, что вы спрашиваете, является деталью реализации, которую вы прячете за открытым интерфейсом этих классов, и поэтому вам не о чем беспокоиться, потому что вы можете изменить его, не меняя интерфейс? Так что давай, попробуй так, как ты предлагаешь. Затем, если есть проблемы с производительностью, памятью или другие проблемы, вы можете исправить реализацию, не нарушая остальной код.

Мне кажется, что хранение ваших данных в базе данных и использование какого-либо объектно-реляционного отображения может быть еще одним вариантом для рассмотрения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...