В моем проекте C ++ у меня есть два класса, Particle
и Contact
. В Particle
классе, у меня есть переменная - член std::vector<Contact> contacts
, содержащий все контакты Particle
объекта, а также соответствующие функции - члены getContacts()
и addContact(Contact cont)
. Таким образом, в «Particle.h» я включаю «Contact.h».
В Contact
классе я хотел бы добавить код в конструктор, Contact
который будет вызывать Particle::addContact(Contact cont)
, чтобы contacts
он обновлялся для обоих Particle
объектов, между которыми Contact
добавляется объект. Таким образом, я должен был бы включить «Particle.h» в «Contact.cpp».
Мой вопрос заключается в том, является ли это приемлемой / хорошей практикой кодирования и, если нет, каков будет лучший способ реализовать то, что я пытаюсь достичь (проще говоря, автоматически обновляя список контактов для конкретной частицы при каждом новом контакте создано).
Эти классы будут связаны вместе Network
классом, который будет иметь N частиц ( std::vector<Particle> particles
) и Nc контактов ( std::vector<Contact> contacts
). Но я хотел иметь возможность иметь такие функции, как particles[0].getContacts()
- нормально ли иметь такие функции в Particle
классе в этом случае, или для этой цели существует лучшая ассоциативная «структура» в C ++ (из двух связанных классов, используемых в другом классе) ,
Мне может понадобиться сдвиг перспективы здесь, как я подхожу к этому. Поскольку два класса связаны между собой Network
объектом класса, это типичная организация кода / класса, когда информация о соединении полностью контролируется Network
объектом (в том смысле, что объект Particle не должен знать о своих контактах и, следовательно, он не должен иметь getContacts()
члена функция). Затем, чтобы узнать, какие контакты имеет конкретная частица, мне нужно было бы получить эту информацию через Network
объект (например, используя network.getContacts(Particle particle)
).
Будет ли менее типичным (возможно, даже не поощряемым) дизайн класса C ++ для объекта Particle также обладать этими знаниями (т. Е. Иметь несколько способов доступа к этой информации - через объект Network или объект Particle, в зависимости от того, что кажется более удобным) )?
источник
Network
объект класса, который содержитParticle
объекты иContact
объекты. Обладая этими базовыми знаниями, я могу затем попытаться оценить, соответствует ли это моим конкретным потребностям, которые все еще изучаются / разрабатываются по мере продвижения в проекте.Ответы:
В вашем вопросе есть две части.
Первая часть - это организация заголовочных файлов C ++ и исходных файлов. Это решается с помощью предварительного объявления и разделения объявления класса (помещая их в файл заголовка) и тела метода (помещая их в исходный файл). Кроме того, в некоторых случаях можно применять идиому Pimpl («указатель на реализацию») для решения более сложных случаев. Используйте указатели совместного владения (
shared_ptr
), указатели единоличного владения (unique_ptr
) и не владеющие указатели (необработанный указатель, то есть «звездочка») в соответствии с лучшими практиками.Вторая часть состоит в том, как моделировать объекты, которые связаны между собой в форме графика . Обычные графы, которые не являются группами DAG (ориентированные ациклические графы), не имеют естественного способа выражения древовидной принадлежности. Вместо этого все узлы и соединения являются метаданными, которые принадлежат одному объекту графа. В этом случае невозможно смоделировать отношения узел-соединение как агрегации. Узлы не «владеют» соединениями; соединения не «владеют» узлами. Вместо этого они являются ассоциациями, и оба узла и соединения «принадлежат» графу. На графике представлены методы запросов и манипуляций, которые работают с узлами и соединениями.
источник
particles[0].getContacts()
- вы предлагаете в своем последнем абзаце, что у меня не должно быть таких функций вParticle
классе, или что текущая структура в порядке, потому что они по своей природе связаны / связаны черезNetwork
? Есть ли лучшая ассоциация "структура" в C ++ в этом случае?network.particle[p]
будет иметь соответствиеnetwork.contacts[p]
с индексами своих контактов. В противном случае Сеть и Частица каким-то образом отслеживают одну и ту же информацию.Particle
объект не должен знать о своих контактах (поэтому у меня не должно бытьgetContacts()
функции-члена), и что эта информация должна поступать только изнутриNetwork
объекта? Будет ли плохой дизайн класса C ++ дляParticle
объекта обладать такими знаниями (то есть иметь несколько способов доступа к этой информации - черезNetwork
объект илиParticle
объект, в зависимости от того, что кажется более удобным)? Последнее, кажется, имеет больше смысла для меня, но, возможно, мне нужно изменить свою точку зрения на это.Particle
знанием чего-либо оContact
s илиNetwork
s состоит в том, что это связывает вас с определенным способом представления этих отношений. Все три класса, возможно, придется договориться. Если вместо этогоNetwork
единственный, кто знает или заботится, это только один класс, который нужно изменить, если вы решите, что другое представление лучше.Particle
иContact
должно быть совершенно отдельно, и связь между ними определяетсяNetwork
объектом. Просто чтобы быть полностью уверенным, это (вероятно) то, что имел в виду @rwong, когда писал (а), что «узлы и соединения« принадлежат »графу. Граф предоставляет методы запросов и манипуляций, которые работают с узлами и соединениями». , правильно?Если я вас правильно понял, один и тот же контактный объект принадлежит более чем одному объекту частиц, поскольку он представляет собой своего рода физический контакт между двумя или более частицами, верно?
Итак, первое, что я считаю сомнительным, - это почему
Particle
переменная-членstd::vector<Contact>
? Это должно бытьstd::vector<Contact*>
илиstd::vector<std::shared_ptr<Contact> >
вместо.addContact
тогда должна иметь другую подпись какaddContact(Contact *cont)
илиaddContact(std::shared_ptr<Contact> cont)
вместо.Это делает ненужным включение «Contact.h» в «Particle.h», предварительное объявление
class Contact
в «Particle.h» и включение «Contact.h» в «Particle.cpp» будет достаточно.Тогда вопрос о конструкторе. Вы хотите что-то вроде
Правильно? Этот дизайн в порядке, если ваша программа всегда знает связанные частицы в тот момент, когда должен быть создан контактный объект.
Обратите внимание, что если вы идете по этому
std::vector<Contact*>
пути, вы должны инвестировать некоторые мысли о сроке службы и владенииContact
объектами. Никакая частица не "владеет" своими контактами, контакт, вероятно, придется удалять только в случаеParticle
разрушения обоих связанных объектов. Использованиеstd::shared_ptr<Contact>
вместо этого решит эту проблему для вас автоматически. Или вы позволяете объекту «окружающего контекста» брать на себя ответственность за частицы и контакты (как это было предложено @rwong) и управлять их временем жизни.источник
addContact(const std::shared_ptr<Contact> &cont)
болееaddContact(std::shared_ptr<Contact> cont)
?move
particle1.getContacts()
иparticle2.getContacts()
доставка одного и того жеContact
объекта, представляющего физический контакт междуparticle1
иparticle2
, а не двух разных объектов. Конечно, можно попытаться спроектировать систему таким образом, чтобы не имело значения, если одновременно доступны дваContact
объекта, представляющих один и тот же физический контакт. Это должно было бы сделатьContact
неизменным, но вы уверены, что это то, что вы хотите?Да, то, что вы описываете, является очень приемлемым способом гарантировать, что каждый
Contact
экземпляр находится в списке контактов aParticle
.источник
То, что вы сделали, правильно.
Другой способ ... Если цель состоит в том, чтобы каждый из
Contact
них был в списке, то вы могли бы:Contact
(частные конструкторы),Particle
класс,Particle
класс другомContact
,Particle
создании фабричного метода, который создаетContact
Тогда вам не нужно включать
particle.h
вcontact
источник
Network
класса, это меняет предложенную структуру, или оно все равно будет таким же?Другой вариант, который вы могли бы рассмотреть, - создать конструктор Contact, который принимает шаблонную ссылку на Particle. Это позволит Контакту добавить себя в любой реализуемый контейнер
addContact(Contact)
.источник