멤버 변수를 클래스 멤버로 참조 m_thing(thing) {}

제 직장에서는이 스타일이 광범위하게 사용되는 것을 봅니다.

#include <iostream>

using namespace std;

class A
{
public:
   A(int& thing) : m_thing(thing) {}
   void printit() { cout << m_thing << endl; }

protected:
   const int& m_thing; //usually would be more complex object
};


int main(int argc, char* argv[])
{
   int myint = 5;
   A myA(myint);
   myA.printit();
   return 0;
}

이 관용구를 설명하는 이름이 있습니까? 나는 그것이 크고 복잡한 객체를 복사하는 데 따르는 큰 오버 헤드를 방지하는 것이라고 가정하고 있습니다.

이것은 일반적으로 좋은 습관입니까? 이 접근 방식에 함정이 있습니까?



답변

이 관용구를 설명하는 이름이 있습니까?

UML에서는 집계라고합니다. 멤버 개체가 참조 클래스에 의해 소유 되지 않는다는 점에서 구성과 다릅니다 . C ++에서는 참조 또는 포인터를 통해 두 가지 방법으로 집계를 구현할 수 있습니다.

나는 그것이 크고 복잡한 객체를 복사하는 데 따르는 큰 오버 헤드를 방지하는 것이라고 가정하고 있습니다.

아니요, 이걸 사용하는 것은 정말 나쁜 이유입니다. 집계의 주된 이유는 포함 된 개체가 포함 된 개체의 소유가 아니므로 해당 수명이 바인딩되지 않기 때문입니다. 특히 참조 된 개체 수명은 참조하는 개체 수명보다 길어야합니다. 훨씬 더 일찍 만들어 졌을 수 있으며 컨테이너의 수명이 끝난 후에도 살 수 있습니다. 그 외에도 참조 된 객체의 상태는 클래스에 의해 제어되지 않지만 외부 적으로 변경 될 수 있습니다. 참조가 아닌 const경우 클래스는 외부에있는 객체의 상태를 변경할 수 있습니다.

이것은 일반적으로 좋은 습관입니까? 이 접근 방식에 함정이 있습니까?

디자인 도구입니다. 어떤 경우에는 좋은 생각이 될 것이고 어떤 경우에는 그렇지 않을 것입니다. 가장 일반적인 함정은 참조를 보유한 개체의 수명이 참조 된 개체의 수명을 초과해서는 안된다는 것입니다. 주변 개체가 참조 된 개체가 소멸 된 참조를 사용하는 경우 정의되지 않은 동작이 발생합니다. 일반적으로 집계보다 컴포지션을 선호하는 것이 좋지만 필요한 경우 다른 도구만큼 좋은 도구입니다.


답변

생성자 주입을 통한 종속성 주입 이라고합니다 . 클래스 A는 종속성을 생성자에 대한 인수로 가져오고 종속 클래스에 대한 참조를 전용 변수로 저장합니다.

wikipedia에 흥미로운 소개가 있습니다.

const 정확성을 위해 다음과 같이 작성합니다.

using T = int;

class A
{
public:
  A(const T &thing) : m_thing(thing) {}
  // ...

private:
   const T &m_thing;
};

그러나이 클래스의 문제점은 임시 객체에 대한 참조를 허용한다는 것입니다.

T t;
A a1{t};    // this is ok, but...

A a2{T()};  // ... this is BAD.

추가하는 것이 더 좋습니다 (적어도 C ++ 11 필요).

class A
{
public:
  A(const T &thing) : m_thing(thing) {}
  A(const T &&) = delete;  // prevents rvalue binding
  // ...

private:
  const T &m_thing;
};


어쨌든 생성자를 변경하면 :

class A
{
public:
  A(const T *thing) : m_thing(*thing) { assert(thing); }
  // ...

private:
   const T &m_thing;
};

임시에 대한 포인터가 없다는 것이 거의 보장 됩니다 .

또한 생성자가 포인터를 사용하기 때문에 사용자가 A전달하는 객체의 수명에주의를 기울여야한다는 것이 사용자에게 더 분명합니다 .


관련 주제는 다음과 같습니다.


답변

이 관용구를 설명하는 이름이 있습니까?

이 사용법에 대한 이름은 없으며 단순히 “Reference as class member”라고 합니다.

나는 그것이 크고 복잡한 객체를 복사하는 데 따르는 큰 오버 헤드를 방지하는 것이라고 가정하고 있습니다.

예, 한 개체의 수명을 다른 개체와 연결하려는 시나리오도 있습니다.

이것은 일반적으로 좋은 습관입니까? 이 접근 방식에 함정이 있습니까?

사용량에 따라 다릅니다. 모든 언어 기능을 사용하는 것은 “강좌를위한 말 선택” 과 같습니다 . 모든 ( 거의 모든 ) 언어 기능이 일부 시나리오에서 유용하기 때문에 존재 한다는 점에 유의하는 것이 중요합니다 .
참조를 클래스 멤버로 사용할 때 유의해야 할 몇 가지 중요한 사항이 있습니다.

  • 참조 된 객체가 클래스 객체가 존재할 때까지 존재하도록 보장해야합니다.
  • 생성자 멤버 이니셜 라이저 목록에서 멤버를 초기화해야합니다. 포인터 멤버의 경우 가능할 수 있는 지연 초기화를 가질 수 없습니다 .
  • 컴파일러는 복사 할당을 생성하지 않으며 operator=()사용자가 직접 제공해야합니다. =이러한 경우 운영자가 취해야 할 조치를 결정하는 것은 번거 롭습니다 . 따라서 기본적으로 클래스는 할당 할 수 없게됩니다 .
  • NULL다른 객체를 참조하기 위해 참조하거나 만들 수 없습니다 . 재 장착이 필요한 경우에는 포인터의 경우와 같이 참조를 사용할 수 없습니다.

가장 실용적인 목적을 위해 (멤버 크기로 인한 높은 메모리 사용을 염려하지 않는 한) 포인터 나 참조 멤버 대신 멤버 인스턴스 만 있으면 충분합니다. 이렇게하면 참조 / 포인터 멤버가 추가 메모리 사용을 희생하면서 가져 오는 다른 문제에 대해 걱정할 필요가 없습니다.

포인터를 사용해야하는 경우 원시 포인터 대신 스마트 포인터를 사용해야합니다. 그것은 포인터로 당신의 삶을 훨씬 더 쉽게 만들 것입니다.


답변

C ++는 클래스 / 구조체를 통해 객체의 수명을 관리하는 좋은 메커니즘을 제공합니다. 이것은 다른 언어에 비해 C ++의 가장 좋은 기능 중 하나입니다.

ref 또는 포인터를 통해 노출 된 멤버 변수가 있으면 원칙적으로 캡슐화를 위반합니다. 이 관용구를 사용하면 클래스의 소비자가 A 객체에 대한 지식이나 제어 권한없이 A 객체의 상태를 변경할 수 있습니다. 또한 소비자가 A 객체의 수명을 넘어서 A의 내부 상태에 대한 참조 / 포인터를 유지할 수 있습니다. 이것은 잘못된 설계입니다. 대신 클래스는 공유 객체에 대한 참조 / 포인터를 보유하도록 리팩터링 될 수 있으며 (소유하지 않음) 생성자를 사용하여 설정할 수 있습니다 (수명 규칙 위임). 공유 객체의 클래스는 경우에 따라 멀티 스레딩 / 동시성을 지원하도록 설계 될 수 있습니다.


답변

회원 참조는 일반적으로 잘못된 것으로 간주됩니다. 그들은 회원 포인터에 비해 삶을 힘들게 만듭니다. 그러나 그것은 특별히 비정상적이지도 않고 특별한 관용구 나 사물도 아닙니다. 그것은 단지 앨리어싱입니다.


답변