[c++] 멤버 데이터에서 포인터 또는 참조를 선호해야합니까?

다음은 질문을 설명하기위한 간단한 예입니다.

class A {};

class B
{
    B(A& a) : a(a) {}
    A& a;
};

class C
{
    C() : b(a) {}
    A a;
    B b;
};

그래서 B는 C의 일부를 업데이트 할 책임이 있습니다. 나는 lint를 통해 코드를 실행했으며 참조 멤버 lint # 1725에 대해 엉망이었습니다 . 여기에서는 기본 복사 및 할당을 충분히 돌보는 것에 대해 이야기하지만 기본 복사 및 할당도 포인터로 나쁘므로 이점이 거의 없습니다.

나체 포인터는 그 포인터를 삭제할 책임이있는 사람을 불확실하게 소개하기 때문에 항상 참조를 사용하려고합니다. 값으로 객체를 포함하는 것을 선호하지만 포인터가 필요한 경우 포인터를 소유하는 클래스의 멤버 데이터에 auto_ptr을 사용하고 객체를 참조로 전달합니다.

포인터가 null이거나 변경 될 수있는 경우 일반적으로 멤버 데이터에 포인터 만 사용합니다. 데이터 멤버에 대한 참조보다 포인터를 선호해야하는 다른 이유가 있습니까?

참조가 초기화 된 후에는 참조를 변경해서는 안되므로 참조가 포함 된 객체를 할당 할 수 없다고 말하는 것이 사실입니까?



답변

참조 멤버는 클래스 구현이 수행 할 수있는 작업 (제한 연산자 할당 방지)을 제한하고 클래스가 제공 할 수있는 이점을 제공하지 않기 때문에 피하십시오.

문제 예 :

  • 각 생성자의 이니셜 라이저 목록에서 참조를 초기화해야합니다.이 초기화를 다른 함수로 인수 분해 할 수있는 방법이 없습니다 ( C ++ 0x까지 편집 : C ++에는 위임 생성자가 있습니다 )
  • 참조는 리 바인드되거나 널이 될 수 없습니다. 이것은 이점이 될 수 있지만 리 바인딩을 허용하거나 멤버가 널이되도록 코드를 변경해야하는 경우 멤버의 모든 사용을 변경해야합니다.
  • 포인터 멤버와 달리 리팩토링에 필요할 수 있으므로 참조를 스마트 포인터 또는 반복자로 쉽게 바꿀 수 없습니다.
  • 참조가 사용될 때마다 값 유형 ( .연산자 등)처럼 보이지만 포인터처럼 작동합니다 (매끄러 워질 수 있음). 예를 들어 Google 스타일 가이드

답변

내 자신의 경험 법칙 :

  • 객체의 수명이 다른 객체의 수명에 의존하기를 원할 때 참조 멤버를 사용하십시오 . 다른 클래스의 유효한 인스턴스가 없으면 객체가 살아남을 수 없다는 명시적인 방법입니다. 할당과 생성자를 통해 참조 초기화를 가져야 할 의무. 인스턴스가 다른 클래스의 멤버인지 아닌지를 가정하지 않고 클래스를 디자인하는 좋은 방법입니다. 당신은 그들의 삶이 다른 사례들과 직접 연결되어 있다고 가정합니다. 나중에 클래스 인스턴스 사용 방법을 변경할 수 있습니다 (새 인스턴스, 로컬 인스턴스, 클래스 멤버, 관리자의 메모리 풀에 의해 생성됨 등).
  • 다른 경우에 포인터 사용 : 나중에 멤버를 변경하려면 포인터 또는 const 포인터를 사용하여 지정된 인스턴스 만 읽도록하십시오. 해당 유형이 복사 가능해야한다면 어쨌든 참조를 사용할 수 없습니다.때로는 특별한 함수 호출 (예 : init ()) 후에 멤버를 초기화해야하며 포인터를 사용하는 것 외에는 선택의 여지가 없습니다. 그러나 : 모든 멤버 함수에 assert를 사용하여 잘못된 포인터 상태를 신속하게 감지하십시오!
  • 객체 수명이 외부 객체의 수명에 의존하고 해당 유형도 복사 가능 해야하는 경우 포인터 멤버를 사용하지만 생성자에서 참조 인수를 사용 하면 생성 에이 객체의 수명이 의존한다는 것을 나타내는 방식으로 표시합니다 인수의 수명 동안 구현은 포인터를 사용하여 여전히 복사 가능합니다. 이러한 멤버가 사본으로 만 변경되고 유형에 기본 생성자가 없으면 유형이 두 목표를 모두 충족해야합니다.

답변

객체는 할당 및 비교와 같은 다른 것들을 허용하지 않아야합니다. ‘부서’, ‘직원’, ‘감독’과 같은 개체가있는 일부 비즈니스 모델을 고려할 경우 한 직원이 다른 직원에게 할당 될 경우를 상상하기 어렵습니다.

따라서 비즈니스 오브젝트의 경우 일대일 관계와 일대 다 관계를 포인터가 아닌 참조로 설명하는 것이 좋습니다.

그리고 아마도 1 또는 0의 관계를 포인터로 설명하는 것이 좋습니다.

따라서 ‘할당 할 수 없습니다’는 없습니다.
많은 프로그래머가 포인터와 함께 사용하기 때문에 참조 사용을 피하기 위해 인수를 찾을 수 있습니다.

회원으로서 포인터를 가지고 있으면 “만약의 경우”설명과 함께 사용하기 전에 사용자 또는 팀 구성원이 포인터를 계속해서 다시 확인해야합니다. 포인터가 0 일 수 있다면 포인터는 아마도 일종의 플래그로 사용되는데, 이는 모든 객체가 고유 한 역할을 수행해야하기 때문에 나쁘다.


답변

가능하면 참조를 사용하고 필요할 때는 포인터를 사용하십시오.


답변

몇 가지 중요한 경우에는 할당 가능성이 필요하지 않습니다. 이들은 종종 범위를 벗어나지 않고 계산을 용이하게하는 경량 알고리즘 래퍼입니다. 이러한 객체는 항상 유효한 참조를 보유하고 복사 할 필요가 없기 때문에 참조 멤버의 주요 후보입니다 .

이러한 경우 할당 연산자 (및 종종 복사 생성자)를 사용할 수 없도록 ( boost::noncopyable 없도록 (비공개 하거나 선언하여) .

그러나 사용자 pts가 이미 언급했듯이 대부분의 다른 객체에서도 마찬가지입니다. 여기서 참조 멤버를 사용하는 것은 큰 문제가 될 수 있으며 일반적으로 피해야합니다.


답변

모든 사람이 일반적인 규칙을 제시하는 것처럼 두 가지를 제안합니다.

  • 절대로 참조를 클래스 멤버로 사용하지 마십시오. 나는 내 자신의 코드에서 그렇게하지 않았으며 (이 규칙에서 옳았다는 것을 스스로에게 증명하지는 않는다) 내가 그렇게 할 경우를 상상할 수 없다. 의미론이 너무 혼란스럽고 실제로 참조가 설계된 것이 아닙니다.

  • 기본 유형을 제외하고 함수에 매개 변수를 전달할 때 또는 알고리즘에 사본이 필요한 경우 항상 참조를 사용하십시오.

이 규칙은 간단하며 좋은 입장에 서있었습니다. 나는 똑똑한 포인터 (그러나 auto_ptr이 아닌 제발)를 다른 사람들의 클래스 멤버로 사용하는 규칙을 남깁니다.


답변

예 : 참조가 초기화 된 후에는 참조를 변경할 수 없으므로 참조를 포함하는 객체를 할당 할 수 없다고 말하는 것이 사실입니까?

데이터 멤버를위한 나의 경험 법칙 :

  • 할당을 막기 때문에 참조를 사용하지 마십시오
  • 클래스가 삭제를 담당하는 경우 boost의 scoped_ptr (auto_ptr보다 안전한)을 사용하십시오.
  • 그렇지 않으면 포인터 또는 const 포인터를 사용하십시오.