2014-04-08 2 views
0

클래스가 부모 개체의 수명 이후에 액세스하면 안되는 개체에 대한 핸들 참조를 반환하는 것은 그리 이상적이지 않습니다. 방어 코딩을 돕기 위해 아래 패턴을 변경하는 가장 좋은 방법은 무엇입니까?반환 된 참조 수명에 대한 C++ 힌팅/경고

// 'A<T>' is a thin factory-style utility class with asynchronous consumers. 
template <typename T> 
struct A { 
    A() : h_(*(new T())) { /* ... */ } 
    ~A() { /* h_ deleted or ownership passed elsewhere */ } 

    // What's the best way to indicate that these handles shouldn't be used after 
    // the destructions of the A instances? 
    T &handle() { return h_; } 

private 
    T &h_; 
}; 

struct B { /* ... */ }; 

int main() { 
    B *b1{nullptr}; 
    { 
    A<B> a; 

    // Is there a good way to trigger detection that the reference is bound to 
    // a variable which will outlive its 'valid' local lifetime? 
    b1 = &a.handle(); 

    B &b2(a.handle()); // this is reasonable though 

    b1->ok_action(); 
    b2.also_alright(); 
    } 
    b1->uh_oh(); 
} 

나는 당신이 진정으로 가장 안전하지 않은 일을에서 C++의 사용자를 막을 수 없습니다 알고 있지만, 이것이 내가 원하는 무엇의 대부분 것처럼 나는 적어도 사소한 우발적 인 사용에 대한 경고를 생성 할 수 있다면 달성하기.

+0

기존 코드를 이해하고 있지만 유지해야하는 항목과 변경할 수있는 내용이 확실하지 않습니다. 포인터 나 참조에'handle()'의 결과를 저장하는 것을 멈춰야한다고 말하고 싶습니다. 직접 (예를 들어'a.handle(). methodOfB()'를 사용하거나'B b = a. – jsantander

+0

@jsantander A <>는 래퍼 파기 시점에서 주변에서 잡히게 될 객체에 대한 평생 매니저 래퍼 (wrapper)이므로 불행히도 복사 의미가 적용되지 않습니다. 항상'a.handle.methodOfB() '를 호출하는 것은 확실히 가능하지만 래퍼보다 수명이 긴 참조를 저장하는 빌드 타임 감지 기능을 추가하여이 까다로운 인터페이스를 조금 더 열악하게 만들었습니다. – Jeff

+0

이러한 참조 대신 std :: weak_ptr을 사용하는 것이 가장 이상적입니다. – berkus

답변

0

개체 A가 클래스 B의 다른 개체를 생성하고 누군가 사용하게 한 다음 A가 나오기 전에 B가 삭제되도록합니다.

B의 인스턴스를 반환하는 대신 A에서 B를 가져 와서 일종의 델리게이트 (가상 메서드, 펑터, 람다 함수)에 전달하는 메서드를 정의 할 수 있습니까? 이 방법으로 사용자 함수는 A 객체의 메소드 호출 내에서 중첩되므로 사용자 코드가 수행하는 작업이 완료되기 전에 A가 파기된다는 것은 논리적으로 불가능합니다. 예컨대

는 :

class A { public: template <typename Functor> void DoSomethingWithAnInstanceOfB(const char* whichB, Functor f) { B& bref = InternalLookupB(whichB); f(bref); } };

이는 올바른 B 인스턴스를 검색 한 다음, 임의의 펑로 전달. functor는 원하는 모든 작업을 수행 할 수 있지만 DoSomethingWithAnInstanceOfB()가 반환되기 전에 필연적으로 반환해야하므로 A의 수명이 적어도 B만큼 길다는 것을 보증합니다.

+0

이 관리 템플릿 클래스에 대해 동적 조회를 수행하는 것은 유감스럽게도 불가능합니다. 'A <>'는 기본적으로 비동기 수신자를위한 팩토리 역할을하므로 소유권 객체가'~ A()'호출을 통과 한 후에도 안전하게 수정하거나 일관성있게 읽을 수 없습니다. 'A '는'Ts' 자체가 스택에 살 수 없으므로'T'를 확장 할 수 없으며 모든 관리 된'T' 클래스를 혼자서 깔끔하게 정리 한 채로 있기 때문에 펑터/함수 typedef를 돌리는 것은 사실이 아닙니다. 우선 순위가 높습니다. 비록 내가이 경우에 그것을 사용할 수 없다하더라도, 당신의 제안을 고맙게 생각합니다. – Jeff

1

나는 당신에 대해 몇 가지 가정을하고 있습니다. 상황 :

  • 핸들은 사용자 임의대로 A에 의해 생성 된 동적으로 할당 된 개체를 가리 킵니다.
  • A이 범위를 벗어난 곳에서 핸들이 전달되므로 A을 필수 게이트웨이로 사용할 수 없습니다.
  • A이 삭제되면 핸들 포인트가 삭제되어야하므로 핸들에 자동 가비지 수집을 적용 할 수 없습니다.
  • 지금까지 컴파일 타임 안전 검사는 불가능한 것 같습니다. 자발적인 충돌보다는 일종의 예외 메커니즘을 통해 런타임에 스스로 코딩하는 실수를 저지르기를 원합니다. 그걸 염두에두고

여기에 가능한 솔루션입니다 :

A의 생성자 내에서, A가 파괴 될 때 설정되는 신호 객체 S의 어떤 종류를 할당합니다. shared_ptr을 사용하여 S을 처리하십시오. A::handleB 핸들과 shared_ptr을 포함하고 S 핸들을 포함하는 사용자 지정 핸들 클래스 H을 반환하십시오. A이 아직 유효한지 (S이 설정되지 않음) 확인하거나 예외를 throw하는 H 내에 역 참조 연산자를 만듭니다. 모든 핸들이 만료되면 S이 자동으로 삭제됩니다.