2012-12-22 6 views
2

기본 클래스에서 파생 된 클래스가 있다고 가정합니다. 다형성을 사용하면 (가상 함수를 사용하여) 기본 클래스에 대한 포인터를 통해 파생 클래스의 함수를 호출 할 수 있습니다. 즉, 기본 클래스의 멤버로 "위장 된"파생 클래스 멤버를 가질 수 있습니다. 이를 염두에두면 생성자의 기본 클래스가 실제로 파생 클래스의 멤버를 구성 할 수 있습니까? (기본 클래스의 멤버로 "척"하므로 아무 것도 부러지지 않습니다)? 논리적으로, 나는 이것이 작동하지 않아야하는 이유가 없다는 것을 알지만 구문 론적으로 어떻게해야하는지 알 수 없다. 이것이 가능하지 않다면, 왜 안 되니?기본 클래스의 생성자 클래스 'C++의 생성자

파생 클래스의 멤버를 구성하고 기본 클래스의 멤버로 반환하는 별도의 함수를 가질 수 있음을 알고 있습니다. 이것이 이것이 불가능 해지면 내가해야 할 일이다. 그러나 그것이 생성자로서 갖는 것이 더 깔끔할 것이다. (그것이이 분리 된 함수가 기본적으로하는 것이기 때문에).

편집 : 다음,이 달성 할 수있는 위에서 언급 한 바와 같이

class base 
{ 
    base() 
    { 
    this=new derived(); //This is what I am looking for 
    } 

    virtual func(); 
}; 

class derived : public base 
{ 
    derived() : base() 
    {} 

    func() 
    { 
    ... 
    } 
}; 

: 실제로

base *fake_base_constructor() 
{ 
    return new derived(); 
} 

, 여러있을 것입니다 여기에 내가 무엇을 찾고의 예입니다 파생 클래스를 만들고 기본 클래스 생성자는 매개 변수를 기준으로 선택하지만 개념적으로는 하나만 있으면됩니다.

+2

'내가 원하는 코드'를 일부 추가하십시오. "기본 클래스의 구성원 인 척"(구성원 데이터도 포함됩니까?)이란 의미는 무엇입니까? "기본 클래스의 구성원으로 반환"한다는 것은 무엇을 의미합니까? – pmr

+0

당신이 묘사하려는 것을 보여주기 위해 최소한 * 시도 *하는 코드가 있습니까? 그리고 다형성은 아무것도 "척하지"않습니다. 캐스팅 *은 그렇지 않은 것처럼 가장합니다. 다형성은 파생 오버라이드를 통한 동작 수정입니다. 템플리트를 통해 정적 폴리 모프 (static-polymorph) 할 수도 있지만, 원하는 것처럼 들리지는 않습니다. – WhozCraig

+0

"아무 것도 부러지지 않는다"는 것은 무엇을 의미합니까? 하위 클래스가 부모의 불변성 (모든 하위 클래스에 의해 유지되어야 함)을 손상시키지 않는다는 것을 보장하는 것과 같은 것 같습니다. 이거 뭡니까? – RonaldBarzell

답변

2

생성자에 뭔가를 전달하여 얻는 유형을 선택하려는 것처럼 들리는가요? 귀하의 예는 정적 선택을하지만 텍스트는 여러 가지 선택이 있음을 의미합니다. 나는 이것을 어떻게 사용할 것인지 궁금합니다.

은이 같은 것을하고 싶었던 말 :

enum Types 
{ 
    Type_A, 
    Type_B, 
}; 

class Base 
{ 
public: 
    Base(Types t) 
    { 
     switch (t) 
     { 
     case Type_A: 
      this = <magic> A; 
      break; 

     case Type_B: 
      this = <magic> B; 
      break; 
     } 
    } 
}; 

을하지만 문제는 C++에서 어떻게 작동이되지 않는 것입니다; 당신의 객체의 공간은 생성자가 적중 될 때 이미 할당되어 있으므로 파생 된 항목을 그 공간에 채울 수는 없습니다. 예를 들어, 이것을 클래스 멤버 변수로 상상해보십시오 :

struct SomeStruct 
{ 
    int i; 
    Base b; 
    float f; 

    SomeStruct(); 
} 

SomeStruct::SomeStruct() : i(4), b(Type_A), f(3.14f) 
{ 
} 

이것은 단지 작동하지 않습니다. 공장 기능을 사용하는 가장 직접적인 해결책이 될 것 같습니다. 그것은 당신이 포인터를 처리해야된다는 사실을 명시하게하고는 프로그래머가 보는 데 사용됩니다 뭔가 : 당신은 호기심 반복 템플릿 패턴과 유사한 뭔가를 할 수

Base* BaseFactory(Types t) 
{ 
    switch (t) 
    { 
    case Type_A: 
     return new A; 

    case Type_B: 
     return new B; 
    } 
} 

하지만 다른 대답으로 정직 제안이 아니다 (부분적으로는 Wikipedia 기사에서 다루지 않는 런타임 다형성을 원하기 때문이기도하지만) 실제로 필요하지는 않습니다. 당신의 솔루션에 관계없이 이러한 것들의 구성을하는 코드 모듈은 생성 될 수있는 모든 것에 대한 지식을 가져야하고 제약 조건을 고려할 때 기본 클래스 이외의 어딘가에 지식 묶음을 갖는 것이 가장 좋습니다. 제 견해로는 큰 묶음의 묶음을 만드는 것을 피하는 것이 가장 안전하다고 생각합니다.

+1

이것은 내가 찾고 있었던 것이다. 고맙습니다. 동적으로 할당되지 않은 객체를 잊어 버리고있었습니다. 위대한 설명, 그리고 의사 소통 기술의 부족에 대한 다른 모든 사람들에게 다시 미안. –

4

원하는 소리는 curiously recurring template pattern입니다.

template <class D> 
class Base 
{ }; 

class Derived : public Base<Derived> 
{ }; 

지금 당신이 Base의 정의에 템플릿 매개 변수 Derived을 사용할 수 있습니다 : 그것은 기본 클래스는 그것을 파생 타입 템플릿 매개 변수를 제공하여 그것에서 파생되는 유형을 알 수 있습니다. 예를 들어, Base의 생성자는 할 수 :

template <class D> 
void Base<D>::some_base_member() 
{ 
    D d; 
}; 

는 사실,이 패턴의 중요한 결과는 당신이 Base 클래스의 Derived 멤버를 호출 할 수 있다는 것입니다. 예를 들어 :

template <class D> 
void Base<D>::some_base_member() 
{ 
    static_cast<D*>(this)->some_derived_member(); 
}; 
+1

_CRTP_의 중요한 부분은'static_cast < Derived* > (this)' –

+0

@ K-ballo입니다. 감사합니다! 나는 그것을 추가하고 있었다. –

+5

물론 'Base'의 생성자에서이를 수행하는 것은 _Undefined Behavior_가 될 것입니다. 'Derived'는 생성자가 완료 될 때까지 존재하지 않기 때문입니다. –

0

당신은 질문 :

“ 기본 클래스 것이 가능하며, 생성자에, 실제로 다음 "척"파생 클래스의 멤버를 (구축 될 수있는 기본 클래스의 멤버, 그래서 아무것도 휴식)? ”

예, 기본 클래스에서 파생 클래스 별 초기화를 수행하는 여러 가지 방법이 있습니다.

#include <stdio.h> 

namespace apiLevel { 
    enum Handle {}; 

    Handle newButton(char const title[]) 
    { 
     printf("apiLevel::newButton(\"%s\")\n", title); 
     return Handle(); 
    } 

    Handle newListbox(char const [] = "") { return Handle(); } 
    Handle newStatic(char const [] = "") { return Handle(); } 
} // namespace apiLevel 

class Widget 
{ 
private: 
    apiLevel::Handle handle_; 

protected: 
    struct ApiWidgetFactory 
    { 
     virtual apiLevel::Handle newWidget(char const title[]) const 
     { 
      return apiLevel::newStatic(title); // Reasonable. 
     } 
    }; 

public: 
    explicit Widget(
     char const     title[] = "\"? (unspecified)\"", 
     ApiWidgetFactory const&  factory = ApiWidgetFactory() 
     ) 
     : handle_(factory.newWidget(title)) 
    {} 
}; 

class Button 
    : public Widget 
{ 
protected: 
    struct ApiWidgetFactory: Widget::ApiWidgetFactory 
    { 
     virtual apiLevel::Handle newWidget(char const title[]) const 
     { 
      return apiLevel::newButton(title); // Derived class specific. 
     } 
    }; 

public: 
    explicit Button(
     char const     title[], 
     ApiWidgetFactory const&  factory = ApiWidgetFactory() 
     ) 
     : Widget(title, factory) 
    {} 
}; 

int main() 
{ 
    Button button("Just a button"); 
} 

위의 방법의 주요 장점은 함께 클라이언트 코드에 형 안전 인터페이스를 제공한다는 것입니다 :

여기 “How to avoid post-construction by using Parts Factories”를받을 그것에 대해 쓴 블로그 게시물에서 하나의 예입니다 일반적인 RAII 지향적 인 구조 (즉, 클래스 불변성을 잘 사용한다).

주요 단점은 두 개의 병렬 클래스 계층 구조를 유지해야한다는 것입니다. 이는 주로 코드를 어디에 넣어야할지에 대한 질문입니다.

자세한 내용은 블로그 게시를 참조하십시오. the FAQ에 링크되어 있으며, 다른 접근법도 있습니다. 블로그 게시는 말의 입에서 곧은 것, 즉 내 것입니다. FAQ 항목은 Marshall이 FAQ에 해당 문제 &을 작성하도록 설득 한 결과입니다.

+0

나는 그것이 내가 찾고있는 것이라고 생각하지 않는다. 내가 틀렸다면 나를 바로 잡으십시오. 그러나 그것은 제가 언급 한 외부 기능 해결책과 기능적으로 같은 것으로 보입니다. (처음으로 자신을 잘 설명하지 않았다면 제 편집을보십시오). –

+0

@bugmenot : 간단한 팩토리 함수 인, 보여준 함수는 매우 다릅니다. 그러한 함수에는 아무 것도 필요하지 않습니다. 아무 것도 추가하지 않기 때문에 (예 : 비 동적 할당을 방지하려는 경우 간단히 소멸자를 보호 할 수 있습니다). 위의 패턴은 'Button'및 'Listbox'와 같은 여러 파생 클래스를 지원합니다. 공통 파생 고유 데이터는 여기에서 기본 클래스 생성자에 의해 초기화 된 API 레벨 위젯에 대한 핸들입니다. –

+0

단 하나의 파생 클래스에서는 쓸모가 없다는 것을 알고 있지만 실제로는 여러 개를 사용하고 생성자 (또는 팩토리 함수)는 매개 변수를 기반으로 선택합니다. 당신이 어떻게 다른지 설명해 주시겠습니까? –