내 C ++ 코드에서 클래스 상호 의존성을 해결하는 방법은 무엇입니까?


10

내 C ++ 프로젝트에서 나는 두 개의 클래스를 가지고 ParticleContact. 에서 Particle클래스 I는 멤버 변수가 std::vector<Contact> contacts(A)의 모든 접점 포함 Particle개체를 대응하는 멤버 함수 getContacts()addContact(Contact cont). 따라서 "Particle.h"에는 "Contact.h"가 포함됩니다.

에서 Contact클래스, 나는 생성자에 코드를 추가 할 Contact그 호출 Particle::addContact(Contact cont)이이 때문에, contacts모두 업데이트됩니다 Particle하는 사이에 개체 Contact개체가 추가되고있다. 따라서 "Contact.cpp"에 "Particle.h"를 포함시켜야합니다.

내 질문은 이것이 허용 가능한 / 좋은 코딩 관행인지 아닌지, 그렇지 않은 경우 달성하려는 것을 구현하는 더 좋은 방법은 무엇입니까? (간단히 말하면 새로운 연락처가있을 때마다 특정 입자의 연락처 목록을 자동으로 업데이트하는 것입니다) 생성됨).


이 클래스는 NetworkN 입자 ( std::vector<Particle> particles)와 Nc 접점 ( std::vector<Contact> contacts) 이 있는 클래스에 의해 연결됩니다 . 그러나 나는 다음과 같은 함수를 가질 수 있기를 원했다 particles[0].getContacts().- Particle이 경우 클래스에서 그러한 함수를 갖는 것이 좋습니까, 또는이 목적을 위해 C ++에서 더 나은 연관 "구조"가 있습니까 (다른 클래스에서 사용되는 두 개의 관련 클래스) .


내가 어떻게 접근하고 있는지에 대한 관점 전환이 필요할 수 있습니다. 두 클래스는 Network클래스 객체 로 연결되어 있기 때문에 일반적인 코드 / 클래스 조직은 Network객체에 의해 전체적으로 제어되는 연결 정보를 갖도록 되어 있습니까 (파티클 객체는 해당 연락처를 인식하지 않아야하며 결과적으로 getContacts()멤버 가 없어야 함) 함수). 그런 다음 특정 입자가 어떤 접촉을 가지고 있는지 알기 위해서는 Network객체를 통해 해당 정보를 얻어야 합니다 (예 :) network.getContacts(Particle particle).

Particle 객체가 그 지식을 갖는 것 (즉, 네트워크 객체 또는 Particle 객체 중 하나를 통해 여러 가지 방법으로 정보에 액세스하는 것이 더 편리한 것)을위한 덜 일반적인 C ++ 클래스 디자인이 아닐까요? )?


4
여기 cppcon 2017에서 이야기입니다 - 헤더의 세 가지 레이어는 : youtu.be/su9ittf-ozk
로버트 안드 제유

3
"최고", "더 나은"및 "허용 가능한"과 같은 단어가 포함 된 질문은 특정 평가 기준을
Robert Harvey

편집 해 주셔서 감사합니다. 문구를 "일반"으로 변경하면 인기의 문제가됩니다. 코딩이 어떤 방식 으로든 수행되는 이유가 있으며 인기는 기술이 "좋은"(일부 "좋은"의 정의)임을 나타내는 것일 수 있지만,화물 경작을 나타내는 것일 수도 있습니다.
Robert Harvey

@RobertHarvey 마지막 섹션에서 "더 나은"및 "나쁜"을 제거했습니다. 객체와 Network객체를 포함 하는 클래스 객체 가있을 때 일반적인 (아마도 선호 / 권장되는) 접근 방식을 요구한다고 가정 합니다. 이 기본 지식을 바탕으로 프로젝트에서 진행하면서 아직 탐색 / 개발중인 특정 요구에 맞는지 평가할 수 있습니다. ParticleContact
AnInquiringMind

@RobertHarvey 나는 "일반적인"것과 "인기있는"것을 배우는 것이 좋으면서 C ++ 프로젝트를 처음부터 완전히 작성하기에 충분하다고 생각합니다. 바라건대 어떤 시점에서 다른 구현이 실제로 더 나은 이유를 알 수 있도록 충분한 통찰력을 얻을 수 있기를 희망하지만 지금은 완전히 본질적으로 접근하지 않도록하고 싶습니다!
AnInquiringMind

답변:


17

귀하의 질문에는 두 부분이 있습니다.

첫 번째 부분은 C ++ 헤더 파일과 소스 파일의 구성입니다. 이것은 포워드 선언 과 클래스 선언 (헤더 파일에 둠)과 메소드 본문 (소스 파일에 둠)을 분리 하여 해결됩니다 . 또한, 어떤 경우에는 더 어려운 경우를 해결하기 위해 Pimpl 관용구 ( "포인터에 대한 포인터") 를 적용 할 수 있습니다 . 모범 사례에 따라 공유 소유권 포인터 ( shared_ptr), 단일 소유권 포인터 ( unique_ptr) 및 비 소유 포인터 (원시 포인터, 즉 "별표")를 사용하십시오.

두 번째 부분은 그래프 형식으로 서로 관련된 개체를 모델링하는 방법 입니다. DAG 가 아닌 일반 그래프 (지향 비순환 그래프)에는 트리와 같은 소유권을 표현하는 자연스러운 방법이 없습니다. 대신 노드와 연결은 모두 단일 그래프 객체에 속하는 메타 데이터입니다. 이 경우 노드 연결 관계를 집계로 모델링 할 수 없습니다. 노드는 연결을 "소유"하지 않습니다. 연결은 노드를 "소유"하지 않습니다. 대신, 이들은 연결이며 노드와 연결 모두 "소유"합니다. 그래프는 노드 및 연결에서 작동하는 쿼리 및 조작 방법을 제공합니다.


답변 주셔서 감사합니다! 실제로 N 입자와 Nc 접촉을 갖는 Network 클래스가 있습니다. 그러나 나는 다음과 같은 기능을 가질 수 있기를 원 particles[0].getContacts()했습니다. 마지막 단락에서 Particle클래스에 그러한 기능이 없어야 하거나 현재 구조가 본질적으로 관련되어 있거나 관련되어 있기 때문에 현재 구조는 괜찮다고 제안 Network합니까? 이 경우 C ++에 더 나은 연관 "구조"가 있습니까?
AnInquiringMind

1
일반적으로 네트워크는 개체 간의 관계에 대한 책임이 있습니다. 예를 들어 인접 목록을 사용하는 경우 입자 network.particle[p]network.contacts[p]해당 접점의 색인과 일치 합니다. 그렇지 않으면 네트워크와 입자가 모두 동일한 정보를 추적합니다.
쓸모없는

@ 쓸모없는 예, 그 과정을 잘 모르겠습니다. 그래서 당신은 Particle객체가 그 접촉을 인식해서는 안되며 (따라서 getContacts()멤버 함수 가 없어야 함 ) 그 정보는 Network객체 내에서만 가져와야한다고 말하고 있습니까? Particle객체가 그 지식을 갖는 것이 나쁜 C ++ 클래스 디자인입니까 (즉, Network객체 또는 객체를 통해 정보에 액세스하는 여러 가지 방법이 Particle더 편리한 것)? 후자는 나에게 더 의미가있는 것처럼 보이지만, 이것에 대한 내 관점을 바꿔야 할 수도 있습니다.
AnInquiringMind

1
@PhysicsCodingEnthusiast : s 또는 s Particle에 대해 아는 것의 문제 는 그것이 당신을 그 관계를 나타내는 특정한 방법으로 묶는다는 것입니다. 세 수업 모두 동의해야 할 수도 있습니다. 대신 알고 있거나 관심을 갖는 유일한 사람인 경우 다른 표현이 더 낫다고 판단되면 변경해야하는 유일한 수업입니다. ContactNetworkNetwork
cHao

@cHao 좋아,이 말이 맞아. 그래서 ParticleContact완전히 분리되어야하고, 이들 사이의 관계는로 정의되는 Network객체. 완전히 확실하게 말하면, 이것은 아마도 @rwong이 "노드와 연결 모두"소유자 "라는 그래프를 쓸 때 의미했던 것입니다. 그래프는 노드와 연결에서 작동하는 쿼리 및 조작 방법을 제공합니다." , 권리?
AnInquiringMind

5

내가 당신에게 맞다면, 같은 접촉 물체는 둘 이상의 입자 물체에 속합니다. 왜냐하면 그것은 둘 이상의 입자 사이에 어떤 종류의 물리적 접촉을 나타 내기 때문입니다.

그래서 내가 의심스러워하는 첫 번째 것은 왜 Particle멤버 변수가 std::vector<Contact>있습니까? 그것은해야 std::vector<Contact*>하거나 std::vector<std::shared_ptr<Contact> >대신합니다. addContact그런 다음 addContact(Contact *cont)또는 다른 서명이 있어야 addContact(std::shared_ptr<Contact> cont)합니다.

따라서 "Particle.h"에 "Contact.h", "Particle.h"에 대한 포워드 선언 class Contact, "Particle.cpp"에 "Contact.h"를 포함하면 충분하지 않습니다.

그런 다음 생성자에 대한 질문입니다. 당신은 같은 것을 원합니다

 Contact(Particle &p1, Particle &p2)
 {
      p1.addContact(this);
      p2.addContact(this);
 }

권리? 접점 개체를 만들어야하는 시점에서 프로그램이 항상 관련 입자를 알고있는 한이 설계는 정상입니다.

std::vector<Contact*>경로 를 따라 가면 Contact객체 의 수명과 소유권에 대한 몇 가지 생각을 투자해야 합니다. 입자가 접촉을 "소유"하지 않으면, 관련된 두 Particle객체가 모두 파괴 된 경우에만 접촉을 삭제해야합니다 . std::shared_ptr<Contact>대신 사용하면 이 문제가 자동으로 해결됩니다. 또는 "서라운드 컨텍스트"객체가 파티클과 연락처 (@rwong에서 제안한 것처럼)의 소유권을 가져와 수명을 관리하게합니다.


나는 addContact(const std::shared_ptr<Contact> &cont)이상의 장점을 보지 못 addContact(std::shared_ptr<Contact> cont)합니까?
Caleth

@Caleth : 이것은 여기서 논의되었다 : stackoverflow.com/questions/3310737/…- "const"는 여기서 중요하지 않지만, 참조로 객체를 전달하는 것은 (그리고 값으로 스칼라) C ++의 표준 관용구입니다.
Doc Brown

2
이러한 답변의 대부분은 사전 move패러다임 에서 온 것 같습니다
Caleth

@ Caleth : 좋아, 모든 nitpickers를 행복하게 유지하기 위해, 나는이 대답의 중요하지 않은 부분을 바꿨다.
Doc Brown

1
@PhysicsCodingEnthusiast : 아니,이 만드는 방법에 대한 최우선이다 particle1.getContacts()particle2.getContacts()같은 전달 Contact사이의 물리적 접촉을 나타내는 개체를 particle1하고 particle2두 개의 서로 다른 개체를하지. 물론 Contact동일한 물리적 접촉을 나타내는 두 개의 객체를 동시에 사용할 수 있는지 여부는 중요하지 않은 방식으로 시스템을 설계하려고 시도 할 수 있습니다. 이것은 불변성을 만드는 것과 관련이 Contact있지만 이것이 당신이 원하는 것이 확실합니까?
Doc Brown

0

예, 설명하는 것은 모든 Contact인스턴스가의 연락처 목록에 있는지 확인하는 매우 수용 가능한 방법입니다 Particle.


답변 주셔서 감사합니다. 한 쌍의 상호 의존 클래스를 사용하지 말아야한다는 제안을 읽었지만 (예를 들어, MS Joshi의 "C ++ 디자인 패턴 및 파생물 요금") 반드시 정확하지는 않습니까? 호기심에서, 상호 의존성을 요구하지 않고이 자동 업데이트를 구현하는 다른 방법이 있습니까?
AnInquiringMind

4
@PhysicsCodingEnthusiast : 상호 의존적 인 수업은 모든 종류의 어려움을 야기하므로 피해야합니다. 그러나 때로는 두 클래스가 서로 밀접하게 관련되어 있기 때문에 상호 의존성을 제거하면 상호 의존성보다 더 많은 문제가 발생합니다.
Bart van Ingen Schenau

0

당신이 한 일은 맞습니다.

다른 방법은 ... 모든 목표가 Contact목록에 있는지 확인하는 것이 목표라면 다음과 같이 할 수 있습니다.

  • Contact(비공개 생성자) 의 블록 생성 ,
  • 앞으로 선언 Particle클래스
  • 만들 Particle클래스의 친구 Contact,
  • 에서 것은 Particle를 생성하는 팩토리 메소드를 만들Contact

그럼 당신은 포함 할 필요가 없습니다 particle.hcontact


답변 주셔서 감사합니다! 이것은 이것을 구현하는 유용한 방법처럼 보입니다. Network클래스 에 관한 초기 질문에 대한 편집으로 제안 된 구조가 변경됩니까? 아니면 여전히 동일합니까?
AnInquiringMind

질문을 업데이트 한 후에는 범위가 변경됩니다. ... 이전에는 기술적 인 문제 였을 때 응용 프로그램의 아키텍처에 대해 묻습니다.
Robert Andrzejuk

0

고려할 수있는 또 다른 옵션은 파티클 참조를 템플릿 화하는 Contact 생성자를 만드는 것입니다. 이를 통해 Contact는 자신을 구현하는 모든 컨테이너에 추가 할 수 있습니다 addContact(Contact).

template<class Container>
Contact(/*parameters*/, Container& container)
{
  container.addContact(*this);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.