2012-09-25 1 views
5

QSharedData을 사용하는 동안 타입 시스템을 만들려고합니다. 아이디어는 간단합니다. 여러 가지 데이터 유형이 있습니다. 각 유형은 기본 추상 클래스에서 파생됩니다. QSharedData을 사용하여 각각의 실제 데이터를 저장하려고하지만 파생 클래스 각각에 다른 데이터가 저장 될 것입니다. 나는 지금 가장 기본적인 예제를 만들고, 몇 가지 문제를 겪고있다. QSharedData and 継承

은의이 내 기본 순수 가상 클래스입니다 가정 해 봅시다 :

class cAbstractData: public QSharedData 
{ 
public: 
    cAbstractData(){ } 
    virtual int type() = 0; 
}; 

class cAbstractValue 
{ 
public: 
    cAbstractValue(){ } 
    virtual int type() = 0; 
protected: 
    QSharedDataPointer<cAbstractData>data_; 
}; 

이제 내가 (인 minmalistic 예와 같은) 하나의 값을 표현하기위한 클래스를 만들고 싶어 가정 해 봅시다. 나는 기본 값 클래스에서 cAtomicValue를 유도하고있어, 나는 또한 값을 유지하기 위해 데이터 클래스를 파생하고 있습니다 : 그것은 잘 작동이 단계에서 이제

class cAtomicData:public cAbstractData 
{ 
public: 
    cAtomicData() { value_ = 0; } 
    int type(){ return 1; } 
    QVariant value_;//the actual value 
}; 

class cAtomicValue:public cAbstractValue 
{ 
public: 
    cAtomicValue() { 
     data_ = new cAtomicData;//creating the data object. 
    } 
    int type(){ return 1; } 
}; 

을하고, 디버거에서 나는 권리를 볼 수 있습니다 포인터 유형. 하지만 이제는 값을 설정하고 가져 오는 함수를 추가하려고합니다. 어떻게해야하는지 이해하지 못합니다. 세터를 예로 들어 봅시다. 값을 설정하려면 cAtomicValue 클래스의 data_ 멤버를 통해 cAtomicData 클래스의 구성원 인 value_에 액세스해야합니다. 그러나 data_이 기본 클래스 포인터 (cAbstractData)를 보유하고 있기 때문에 어떻게 든 올바른 유형 (cAtomicData)으로 캐스팅해야합니다. 나는이 일을 시도 :

template<class T> void set(T value) 
{ 
    static_cast<cAtomicData*>(data_.data())->value_ = value; 
} 

가 분명히 작동하지 않습니다, 그것은 detach()라고하고 기본 클래스는 가상 순수 할 수 없기 때문에 기본 클래스의 사본을 시도하기 때문이다. 그런 다음 포인터 자체를 캐스팅하려고 :

static_cast<cAtomicData*>(data_)->value_ = value; 

을하지만, 나는 invalid static_cast ... 오류를 받고 있어요.

어떻게하면됩니까? 근본적으로 올바른 방법으로 수행하고 있습니까?

답변

2

여기서 시도하는 것을 달성 할 방법이 없습니다. 발견 한대로 QSharedDataPointer에 포함 된 실제 유형을 템플릿으로 작성해야합니다.

기본 클래스를 템플릿으로 만들 수 있습니다.

template<class T> 
class cAbstractValue 
{ 
public: 
    cAbstractValue(){ } 
    virtual int type() = 0; 
protected: 
    QSharedDataPointer<T> data_; 
}; 

그러나 나는 당신이 그로부터 얻을 수있는 이점을 확신 할 수 없습니다.

+1

감사합니다. 분명히 실제로 방법이 없습니다. 결국'QSharedPointer'를 대신 사용했습니다. – SingerOfTheFall

5

QSharedDataPointer 대신 QExplicitlySharedDataPointer으로 전환 할 수 있습니다. 이런 식으로 cAbstractData 개체에 대한 비 const 포인터를 얻으려고 할 때마다 detach()이 호출되지 않습니다. QExplicitlySharedDataPointer<cAbstractData> 개체를 QExplicitlySharedDataPointer<cAtomicData> 개체로 캐스팅하는 작업이 포함됩니다. 그러나 copy-on-write를 사용하려는 경우 cAbstractData을 수정할 때마다 수동으로 detach()으로 전화해야합니다. 어쩌면 당신을 위해 분리를 수행하는 래퍼 클래스를 작성할 수 있습니다. QExplicitlySharedDataPointerQSharedPointer의 크기 (this blog entry 참조) 회 동안 (이진 Compability가 유지 그러므로 이상) 일반적인 포인터와 동일한 크기이기 때문에

이 방법

QSharedPointer를 사용 통해 선호 될 수있다.

편집 : 당신이 (또는 서브 클래스의) 실제로 참조되는 객체 유형 cAtomicData의 목적은 보장해야합니다, 그래서 QExplicitlySharedDataPointer<cAtomicData>-QExplicitlySharedDataPointer<cAbstractData>에서 캐스팅, 정적주의, 또는 행동 포인터를 사용하는 것은 정의되지 않을 수 있습니다.

2

내 응용 프로그램에서 비슷한 문제가 있었는데 여기 어떻게 해결 했는가? 나는 BaseClass을 가지며, 이는 Pimpl 관용구를 사용하여 구현되었고 QExplicitlySharedDataPointerBaseClassPrivate을 가리 킵니다. 이 클래스는 DerivedClass이 상속되며 BaseClassPrivate을 상속하는 DerivedClassPrivate 인 전용 멤버를 상속받습니다.

BaseClassPrivate에는 baseParam이라는 float 멤버가 있고 DerivedClassPrivate에는 derivedParam이라는 또 다른 float 매개 변수가 있습니다.

나는 일이 문제를 해결 다음

  1. 가상 clone() 정의이 DerivedClassPrivate

  2. 에 대한 포인터와 새로운 파생 클래스를 인스턴스화하는 데 사용됩니다 BaseClass(BaseClassPrivate* p)

    보호 생성자를 정의 방법 모두 BaseClassPrivateDerivedClassPrivate

    이 메서드는 딥 복사가 필요할 때마다 개인 클래스를 올바르게 복사하기 위해 호출됩니다. 따라서 'QExplicitlySharedDataPointer :: detach()'를 호출하는 대신 QSharedData 참조 카운터가 1보다 큰지 확인한 다음 복제본을 호출합니다. QSharedData :: ref는 문서에 없으므로 언제든지 변경 될 수 있습니다 (곧 발생하지는 않지만).

  3. 정적 내가 편리 개인 dCasted() 함수를 정의 찾을 DerivedClass

    의 D 포인터를 캐스팅.

그에 따라 하나 또는 baseParam derivedParam를 반환 foo()BaseClassPrivateDerivedClassPrivate에 도입이 가상 기능을 테스트한다.

BaseClass.h

class BaseClass 
{ 
public: 
    BaseClass() : d(new BaseClassPrivate()) {} 
    BaseClass(const BaseClass& other) : d(other.d) {} 
    BaseClass& operator =(const BaseClass& other) {d = other.d; return *this;} 
    virtual ~BaseClass() {} 

    float baseParam() const {return d->baseParam;} 
    void setBaseParam(float value) { 
     detach(); // instead of calling d.detach() 
     d->baseParam = value; 
    } 

    float foo() const {return d->foo();} 

protected: 
    BaseClass(BaseClassPrivate* p) : d(p) {} 
    void detach() { 
     // if there's only one reference to d, no need to clone. 
     if (!d || d->ref == 1) return; // WARNING : d->ref is not in the official Qt documentation !!! 
     d = d->clone(); 
    } 
    QExplicitlySharedDataPointer<BaseClassPrivate> d; 
}; 

DerivedClass.h

class DerivedClass : public BaseClass 
{ 
public: 
    DerivedClass() : BaseClass(new DerivedClassPrivate()) {} 

    float derivedParam() const {return dCasted()->derivedParam;} 
    void setDerivedParam(float value) { 
     detach(); // instead of calling d.detach(); 
     dCasted()->derivedParam = value; 
    } 

private: 
    DerivedClassPrivate* dCasted() const {return static_cast<DerivedDataPrivate*>(d.data());} 
}; 

BaseClassPrivate.h

class BaseClassPrivate : public QSharedData 
{ 
public: 
    BaseClassPrivate() : QSharedData(), baseParam(0.0) {} 
    BaseClassPrivate(const BaseClassPrivate& other) : 
     QSharedData(other), baseParam(other.baseParam) {} 
    virtual ~BaseClassPrivate() {} 

    float baseParam; 
    virtual float foo() const {return baseParam;} 

    virtual BaseClassPrivate* clone() const { 
     return new BaseClassPrivate(*this); 
    } 
}; 

DerivedClassPrivate : 여기

코드이다.

전화 가상 함수 :

DerivedClass derived; 
derived.setDerivedParam(1.0); 
QCOMPARE(derived.foo(), 1.0); // proving that DerivedClassPrivate::foo() is called 

확인 사본 DerivedClass에서 제대로 BaseClass에 :

BaseClass baseCopy = derived; 
QCOMPARE(baseCopy.foo(), 1.0); // proving that DerivedClassPrivate::foo() is called 
           // even after copying to a BaseClass 

은에서 복사본을 이제 시간

class DerivedClassPrivate : public BaseClassPrivate 
{ 
public: 
    DerivedClassPrivate() : BaseClassPrivate(), derivedParam(0.0) {} 
    DerivedClassPrivate(const DerivedClassPrivate& other) : 
     BaseClassPrivate(other), derivedParam(other.derivedParam) {} 

    float derivedParam; 
    virtual float foo() const {return derivedParam;} 

    virtual BaseClassPrivate* clone() const { 
     return new DerivedClassPrivate(*this); 
    } 
}; 

, 우리는 다음과 같은 일을 할 수 BaseClass ~ BaseClass res 원래 클래스를 pecting하고 또한이 기록 중 복사 올바르게합니다

BaseClass bbCopy(baseCopy);  // make a second copy to another BaseClass 
QCOMPARE(bbCopy.foo(), 1.0); // still calling DerivedClassPrivate::foo() 

// copy-on-write 
baseCopy.setBaseParam(2.0);  // this calls the virtual DerivedClassPrivate::clone() 
           // even when called from a BaseClass 
QCOMPARE(baseCopy.baseParam(), 2.0); // verify the value is entered correctly 
QCOMPARE(bbCopy.baseParam(), 1.0); // detach is performed correctly, bbCopy is 
             // unchanged 
QCOMPARE(baseCopy.foo(), 1.0); // baseCopy is still a DerivedClass even after detaching 

희망이 당신이 당신의 유형에 대한 ::clone() function을 구현할 수 Qt는 4.5 이후

+0

개인 데이터의 참조 횟수가 1이라도 BaseClass에서 setBaseParam()을 호출하면 전체 복사본이 만들어지지 않습니까? 참조가 1보다 클 때 먼저 clone()을 호출하는 것이 더 좋지 않습니까? – UndeadKernel

+0

예, 당신은 절대적으로 옳습니다. 나는 당신의 코멘트를 보았을 때 이것을 생각하고있었습니다. QSharedData를 사용하여 참조 카운트를 알 수있는 방법이 없기 때문에 불행히도 이것은주의해야합니다. 댄이 제안한 기본 클래스를 템플릿으로 만드는 것이 최선의 방법이라고 생각합니다. –

+0

http://stackoverflow.com/questions/2693319/qexplicitlysharedpointer-and-inheritance –

0

하는 데 도움이 :

이 기능은이다 자신의 유형에 대해 "가상 복사본 생성자"를 지원할 수 있도록 제공됩니다. 그래서, 당신이 아래의 예처럼, 자신의 유형이 함수의 템플릿 전문화를 선언해야하기 위하여 : 위의 예에서

template<> 
EmployeeData *QSharedDataPointer<EmployeeData>::clone() 
{ 
    return d->clone(); 
} 

, 클론() 함수에 대한 템플릿 특수화는 EmployeeData를 호출 : clone() 가상 함수. EmployeeData에서 파생 된 클래스는 해당 함수를 재정의하고 적절한 다형성 유형을 반환 할 수 있습니다.

이 기능은 Qt 4.5에서 처음 소개되었습니다.

나는 그렇게했습니다.

은 어느 당신의 추상 기본 클래스와 모든 파생 클래스는 QSharedDataPointer::clone()에서 호출 거라고 virtual BaseClass* clone() 기능을 구현해야하거나 d 같은 내용으로 새로운 인스턴스를 생성하는 다른 방법 (예를 들어 공장)가 필요합니다.