2013-03-21 3 views
16

C++ Rule of Three에 대해 많이 읽었습니다. 많은 사람들이 그것을 맹세합니다. 그러나 규칙이 명시 될 때 예외가 있음을 나타내는 "보통", "가능성 있음"또는 "아마도"와 같은 단어가 거의 항상 포함됩니다. 나는이 예외적 인 경우가 무엇인지에 대한 많은 논의를 보지 못했다. 3 가지 규칙이 성립하지 않는 경우 또는 적어도 그 규칙을 준수하는 것이 이점을 제공하지 않는 경우이다.3의 규칙에 대한 예외?

제 질문은 제 상황이 제 3 규칙의 정당한 예외인지 여부입니다. 필자는 아래에서 설명한 상황에서 명시 적으로 정의 된 복사 생성자와 복사 할당 연산자가 필요하다고 생각하지만 기본 (암시 적으로 생성 된) 소멸자는 정상적으로 작동합니다. 여기에 내 상황이있다 :

두 개의 클래스 A와 B가있다. 여기에있는 질문은 A. B는 A의 친구이다. A에는 B 개체가 들어있다. B는 B 오브젝트를 소유하는 A 오브젝트를 가리 키기위한 A 포인터를 포함합니다. B는이 포인터를 사용하여 A 객체의 전용 멤버를 조작합니다. B는 A 생성자를 제외하고 결코 인스턴스화되지 않습니다. 이처럼 :

// A.h 

#include "B.h" 

class A 
{ 
private: 
    B b; 
    int x; 
public: 
    friend class B; 
    A(int i = 0) 
    : b(this) { 
     x = i; 
    }; 
}; 

그리고 ... 내가 그렇게 내 클래스를 설정하는 것이 왜

// B.h 

#ifndef B_H // preprocessor escape to avoid infinite #include loop 
#define B_H 

class A; // forward declaration 

class B 
{ 
private: 
    A * ap; 
    int y; 
public: 
    B(A * a_ptr = 0) { 
     ap = a_ptr; 
     y = 1; 
    }; 
    void init(A * a_ptr) { 
     ap = a_ptr; 
    }; 
    void f(); 
    // this method has to be defined below 
    // because members of A can't be accessed here 
}; 

#include "A.h" 

void B::f() { 
    ap->x += y; 
    y++; 
} 

#endif 

? 나는 좋은 이유가 있다고 약속한다. 이 클래스는 실제로 여기에 포함 된 것보다 더 많은 일을합니다.

나머지는 쉽습니다. 그렇죠? 자원 관리가 없으며 Big Three도 문제가 없습니다. 잘못된! A에 대한 디폴트 (내재적) 복사 생성자는 충분하지 않습니다. 우리는이 작업을 수행 할 경우

A a1; 
A a2(a1); 

우리는 새로운 a2.b.ap 의미 a2.ba1.b 동일 의미 a1 동일 인 객체 a2는 여전히 a1 가리키는 얻을! 이것은 우리가 원하는 것이 아닙니다. A에 대한 복사 생성자를 정의해야합니다.이 복사 생성자는 기본 복사 생성자의 기능을 복제 한 다음 새 A 객체를 가리 키도록 새 A::b.ap을 설정합니다. 우리는 class A이 코드를 추가

public: 
    A(const A & other) 
    { 
     // first we duplicate the functionality of a default copy constructor 
     x = other.x; 
     b = other.b; 
     // b.y has been copied over correctly 
     // b.ap has been copied over and therefore points to 'other' 
     b.init(this); // this extra step is necessary 
    }; 

복사본 할당 연산자는 같은 이유로 필요하며, 기본 복사 할당 연산자의 기능을 복제 한 후 b.init(this);를 호출하는 동일한 프로세스를 사용하여 구현된다.

그러나 명시 적 소멸자가 필요하지 않습니다. 이 상황은 규칙 3에 대한 예외입니다. 내가 맞습니까?

+0

우리는 이러한 유형의 질문이 더 필요합니다. – Shoe

+9

대문자가 뒤 따르는 모든 밑줄이 시스템 용으로 예약되어 있으므로 포함 보호 _B는 불법입니다. – metal

+5

C++ 11의 경우, 제로 규칙이 더 좋습니다 : http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html이 경우 A와 B의 수명을 관리 할 수 ​​있습니다. B std :: unique_ptr, std :: shared_ptr 및 여기에 std :: weak_ptr (또는 유사한 소유 클래스)를 사용합니다. 6 개월 후에 당신의 코드 독자들에게 그 모든 수수께끼를 풀어줍니다. – metal

답변

9

"규칙 3"에 대해 걱정하지 마십시오. 규칙은 맹목적으로 복종하지 않아도됩니다. 그들은 당신을 생각하게하기 위해 거기에 있습니다. 당신은 생각했습니다. 그리고 당신은 소멸자가 그것을하지 않을 것이라고 결론을 내 렸습니다. 그러니 글을 쓰지 마십시오. 규칙은 존재하지 않으므로 은 자원을 누설하고 소멸자를 쓰는 것을 잊어 버리는을 잊지 마십시오.

이 디자인은 B :: ap가 잘못 될 가능성을 만듭니다. 이것은 잠재적 인 버그의 전체 클래스입니다. 이러한 클래스가 단일 클래스이거나 더 견고한 방법으로 함께 묶여 있다면 제거 될 수 있습니다.

+3

사실 저는 포스터 질문 포스터가 여기서 취한 접근 방식에 동의합니다. 즉 : 모든 사람이 동의하는 것으로 보이는 규칙이 있고 그것을 깨뜨릴 생각을하는 경우 생각하지 말고 조언 *을 요청하십시오. 당신이 받아 들여진 관행에서 벗어나게되면 함부로 빠져 나갈 수있는 위험이 따르기 때문에 수용된 관행이 피하기 위해 개발되었으므로 많은 사람들의 지혜의 축적 된 지혜를 버리는 것을 주저합니다. –

4

B은 강력하게 A에 결합되어 있으며 항상 포함하고있는 A 인스턴스를 사용해야합니까? A에는 항상 B 인스턴스가 포함되어 있습니까? 그리고 그들은 우정을 통해 서로의 사적인 멤버들에게 접근합니다.

그러므로 왜 그들이 별개의 클래스인지 전혀 궁금합니다.

하지만, 여기에 몇 가지 다른 이유로 두 개의 클래스를 필요로 가정의 모든 생성자/소멸자 혼란을 없애는 간단한 수정 프로그램입니다 :

class A; 
class B 
{ 
    A* findMyA(); // replaces B::ap 
}; 

class A : /* private */ B 
{ 
    friend class B; 
}; 

A* B::findMyA() { return static_cast<A*>(this); } 

당신은 여전히 ​​봉쇄를 사용하고 A의 인스턴스를 찾을 수 B에서 offsetof 매크로를 사용하는 this 포인터. 하지만 그것은 static_cast을 사용하고 포인터를 계산하는 컴파일러를 등록하는 것보다 더 지저분합니다.

+0

흠, 나는 상속을 그런 식으로 생각한 적이 없었습니다. 이 예제에서,'findMyA()'만이 상속의 목적입니다. 맞습니까? 그것은 나를 조금 불안하게 만든다. 어쩌면 나는 그런 종류의 우아함을 다룰 수 없을지도 모른다. –

+0

@Sam : 오브젝트 레이아웃과 관련하여 실제로 작은 변화가 있습니다. 개인용 서브 오브젝트를 개인용 기본 오브젝트로 대체했습니다. –

+0

아, 그건 사유 유전입니다. 나는 전에 그것을 만난 적이 없다. 항상 배워야 할 새로운 것들. 특징 부러워하는 –

2

@dspeyer와 함께합니다. 당신은 생각하고 결정합니다. 사실 누군가는 이미 보통 3 가지 규칙 (설계 중에 올바른 선택을하는 경우)이 2 가지 규칙으로 내려 간다는 결론을 내 렸습니다. 리소스를 위에서 설명한 스마트 포인터와 같은 라이브러리 개체로 관리하고 대개는 제거 할 수 있습니다 폐물 소각로. 운이 좋다면 모든 것을 제거하고 컴파일러를 사용하여 코드를 생성 할 수 있습니다.

사이드 노트 : 귀하의 복사 생성자는 컴파일러가 생성 한 복제하지 않습니다. 컴파일러가 복사 생성자를 사용하는 동안 복사 할당을 사용합니다. 생성자 본문에서 할당을 제거하고 초기화 프로그램 목록을 사용하십시오. 더 빠르고 깨끗합니다.

멋진 질문, 멋진 답변 양식 벤 (내 동료를 직장에서 혼동시키는 또 다른 속임수)과 나는 당신에게 두 줄의 상을주는 것을 기쁘게 생각합니다.

+0

아, 당신은 복사 생성자에 대한 권리입니다. 그것은 감독이었습니다. 감사. 그것을 Upvote. –

관련 문제