제 직장에서는이 스타일이 광범위하게 사용되는 것을 봅니다.
#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의 내부 상태에 대한 참조 / 포인터를 유지할 수 있습니다. 이것은 잘못된 설계입니다. 대신 클래스는 공유 객체에 대한 참조 / 포인터를 보유하도록 리팩터링 될 수 있으며 (소유하지 않음) 생성자를 사용하여 설정할 수 있습니다 (수명 규칙 위임). 공유 객체의 클래스는 경우에 따라 멀티 스레딩 / 동시성을 지원하도록 설계 될 수 있습니다.
답변
회원 참조는 일반적으로 잘못된 것으로 간주됩니다. 그들은 회원 포인터에 비해 삶을 힘들게 만듭니다. 그러나 그것은 특별히 비정상적이지도 않고 특별한 관용구 나 사물도 아닙니다. 그것은 단지 앨리어싱입니다.