2014-04-30 1 views
20

나는 생성자와 다른 init 기능을 가진 클래스가있는 라이브러리를 사용하고 있습니다. init 이후지금부터 _const 변수를 만들 수 있습니까?

MyClass a; 
a.init(); 

이 (나는 const MyClass a을 쓸 수 없습니다) CONST 인스턴스를 생성에서 저를 방지 const되지 않습니다 : 때마다 나는 예를 들어, 호출 할 필요가 새로운 인스턴스를 만든다. init을 호출 한 다음 "here on out"에서 선언합니다 (범위의 나머지 부분을 추측합니다). 내 변수는 const입니까?

이 작동하지만, 원래의 변수를 접촉 여부에 의존 :

MyClass dont_touch; 
dont_touch.init(); 
const MyClass & a = dont_touch; 
+0

대답은 '아니오'입니다. 너처럼 나도 그랬 으면 좋겠다. – Mehrdad

+0

'init' 호출에'const_cast'를 사용하여 빠르고 더러운 작업을 수행 할 수도 있습니다. 즉,'const' 객체를 선언하고'init' 호출을 위해 객체 주위에'const_cast'를 선언하십시오. 그러나'const_cast'는 일반적으로 나쁜 습관으로 여겨지고 있습니다. 나는 [templated solution] (http://stackoverflow.com/a/23400028/3100771)이 슈퍼 매끄럽다고 생각합니다. – Apriori

+1

@Apriori 확실히 정의되지 않은 동작. const_cast를 정의 된 const 객체에서 const_cast 없애고 const가 아닌 내용을 처리 할 수 ​​없습니다. –

답변

16

당신은 당신이 결코 변경 가능한 객체에 대한 외부 액세스를 제공하지 않습니다 동안 장소에서 초기화를 유지할 수 있습니다

const MyClass ConstantVal = []{ 
    MyClass a; 
    a.init(); 
    return a; 
}(); 

람다 기능을 사용할 수있는 C++ (11)를 사용하는 경우. http://herbsutter.com/2013/04/05/complex-initialization-for-a-const-variable/

+1

컴파일러는 객체가 수정되지 않았다고 가정 할 수 있기 때문에 이것이 내 솔루션보다 더 최적화 될 수 있다고 생각합니다. –

+0

내 특정 질문에 대한 답변입니다. 나는 a.init()가 람다에 합리적으로 적합하지 않을 수도있는 임의의 연산으로 대체되는 좀 더 일반적인 질문을 할 수있다. –

5

는 처음 두 줄을 래핑하고 당신에게 갈 준비가되어 객체를 제공하는 함수를 만듭니다.

MyClass makeMyClass() 
{ 
    MyClass a; 
    a.init(); 
    return a; 
} 

// Now you can construct a const object or non-const object. 
const MyClass a = makeMyClass(); 
MyClass b = makeMyClass(); 

업데이트

사용 makeMyClass()는 매번 함수가 호출되는 임시 객체의 생성과 소멸을 포함한다.

MyClass const& makeMyClass() 
{ 
    static bool inited = false; 
    static MyClass a; 
    if (!inited) 
    { 
    inited = true; 
    a.init(); 
    } 
    return a; 
} 

그것은 앞에서 설명한대로 계속 작동, 사용의 : 그 상당한 비용 될 경우, makeMyClass()이로 변경할 수 있습니다. 또한 다음 작업을 수행 할 수 있습니다.

const MyClass& c = makeMyClass(); 
+0

이것은 비용이 듭니다. 맞습니까? –

+1

예, 복사 비용이 부과됩니다. –

+3

@mangledorf이 문제에 대해서는 잘 모르겠지만 [반환 값 최적화] (https://en.wikipedia.org/wiki/Return_value_optimization)가 유용 할 수 있습니다. 그래서 만약 내가'-O3'을 사용한다면, 당신이 이것에 대해 걱정할 필요가 없다고 생각합니다. 그냥 생각. – gongzhitaao

9

래퍼 클래스를 만들어 대신 사용할 수 있습니다.

class WrapperClass : public MyClass 
{ 
public: 
    WrapperClass() 
    { 
     init(); // Let's hope this function doesn't throw 
    } 
}; 

또는

class WrapperClass 
{ 
public: 
    WrapperClass() 
    { 
     m_myClass.init(); // Let's hope this function doesn't throw 
    } 
    operator MyClass&() {return m_myClass;} 
    operator const MyClass&() const {return m_myClass;} 
private: 
    MyClass m_myClass; 
}; 

을 MyClass의 인스턴스를 포함하는 클래스를 작성 또는이 문제를 해결하기 위해 템플릿을 쓰기 : MyClass에 가상 소멸자가있는 경우

당신은 다음과 같이 그것에서 안전 유도를 느낄 수있다 위의 두 가지 해결책 중 하나를 사용하여 일반적인 문제 : 예.

template <class T> class WrapperClass : public T 
{ 
public: 
    WrapperClass() 
    { 
     T::init(); 
    } 
}; 

typedef WrapperClass<MyClass> WrapperClass; 
+0

아마도 파생 클래스가 원하는 경우 사용되는 함수 내에서 정의 될 수 있다는 점에 유의할 가치가 있습니다. 그러나 템플릿 솔루션을 사용하는 것이 더 좋으며이를 회피합니다. 의지에 따라 인스턴스를 만들 수 있기 때문입니다. – Apriori

+1

'init'을 던지면 괜찮을 것입니다. 오류 코드가 반환되지 않기를 바랍니다. 'init' 함수의 일반적인 이유는 개발자가 에러가 발생할 수 있다는 것을 알고 있지만 예외를 알지 못하기 때문에 개발자가 에러를보고 할 수 없다는 것입니다. – MSalters

1

당신은 실제로 심지어 C++ 11 람다없이, 아주 간단하게 수행 할 수 있습니다 : 도 볼 수 const_cast의 사용은 해킹의 비트가 인정 하듯이

const MyClass a; 
{ 
    MyClass _a; 
    _a.init(); 
    std::swap(const_cast<MyClass&>(a), _a); 
} 

입니다

하지만 '수상 const은 아주 약한 지정자이므로 아무 것도 부러 뜨리지 않습니다. 동시에 MyClass 개체는 스왑되지 않고 복사되지 않으므로 (복사 할 수있는 가장 값 비싼 개체는 swap 기능을 제공하고 std::swap의 과부하를 주입해야 함) 매우 효율적입니다.

struct Construct_Init { 
    operator MyClass() const 
    { 
     MyClass a; 
     a.init(); 
     return a; 
    } 
}; 
const MyClass a = Construct_Init(); 

이 함수에서 다음과 같이 할 수 있습니다합니다 (Construct_Init 구조 네임 스페이스 범위에서 선언 할 필요가 없다)하지만, 조금 이상 : 캐스트없이

, 그것은 도우미를 필요로한다. Copy Elision을 사용하여 개체의 복사본을 최적화 할 수도 있고 최적화하지 않을 수도 있습니다.

두 경우 모두 init()의 반환 값이 손실됩니다. 적절하게 오류를 처리해야합니다 단지

if(!a.init()) 
    throw std::runtime_error("MyClass init failed"); 

또는 : true 성공이고 false이 실패 곳은 부울을 반환하는 경우, 더 나은 것입니다.

관련 문제