2015-11-10 75 views
3

我有一個Data類和一個Wrapper類,它提供訪問Data的方法。 WrapperMutable類擴展Wrapper添加方法來修改Data通過擴展添加可變性

#include <memory> 
using namespace std; 

class Data { 
public: 
    void update(); // non-const method which modifies internal state of Data 
}; 

class Wrapper 
{ 
public: 
    Wrapper(shared_ptr<Data const> data) : myData(data) {} // accepts const pointer 
    // ...bunch of functions providing read-only access to aspects of the Data... 
protected: 
    shared_ptr<Data const> myData; // stores const pointer 
}; 

// Extend Wrapper with methods to modify the wrapped Data. 
class WrapperMutable : public Wrapper 
{ 
public: 
    WrapperMutable(shared_ptr<Data> data) : Wrapper(data) {} 

    // ERROR: invoking non-const method on const object: 
    void updateData() { myData->update(); } 
}; 

當然,問題是與const -ness包裹Data對象的,這意味着WrapperMutable不能修改它。

我認爲改變Wrapper接受和儲存非constData,但通常是客戶本身將只需要const Data訪問,所以那麼他們將被迫const_cast或以創造一個Wrapper複製。

所以我已經能夠實現這一目標的唯一途徑是通過保持一個附加的非const指針在WrapperMutable類,並使用該在可變背景:

class WrapperMutable : public Wrapper 
{ 
public: 
    WrapperMutable(shared_ptr<Data> data) : Wrapper(data), myMutableData(data) {} 

    // Use myMutableData instead of the const myData 
    void updateData() { myMutableData->update(); } 

private: 
    shared_ptr<Data> myMutableData; // non-const pointer to the same Data as in Wrapper 
}; 

有沒有更好的辦法?顯然,從Wrapper得到WrapperMutable是我的問題的來源,但我不想在WrapperMutable中重寫所有Wrapper的方法。

+0

你就不能更改'shared_ptr的 myData'到'常量的shared_ptr myData'?這也允許定義'Wrapper(Data * const data):myData(data){}' –

+0

「,但客戶端本身通常只能訪問'const Data'」 - 我認爲這是導致問題。如果他們不能修改數據,那麼也不應該將該數據傳遞給任何可以修改它的人。 – Petr

+0

@SimonKraemer我認爲這仍然會強制客戶端持有指向'const Data'的指針來執行'const_cast',不是嗎? – atkins

回答

1

繼承表示「種類」關係。

恆常不是「種類」關係。

一個常量事物是一個非常不同的事物,一個可變的事物。

shared_ptr本身的模型顯示瞭如何表達這種關係。可變數據的shared_ptr可轉換爲const Data的shared_ptr,但不能以其他方式轉換。

您可以表達這種關係這樣的:

#include <iostream> 
#include <memory> 

struct Data 
{ 
}; 

struct const_data_wrapper 
{ 
    const_data_wrapper(std::shared_ptr<const Data> p) : _impl(std::move(p)) {} 

    void which() const { 
     std::cout << "const" << std::endl; 
    } 

private: 
    std::shared_ptr<const Data> _impl; 
}; 

struct data_wrapper 
{ 
    data_wrapper(std::shared_ptr<Data> p) : _impl(std::move(p)) {} 

    const_data_wrapper as_const() const { 
     return const_data_wrapper(_impl); 
    } 

    void which() const { 
     std::cout << "not const" << std::endl; 
    } 

private: 
    std::shared_ptr<Data> _impl; 
}; 

using namespace std; 

auto main() -> int 
{ 
    auto w1 = data_wrapper(make_shared<Data>()); 
    auto w2 = w1.as_const(); 

    w1.which(); 
    w2.which(); 

    return 0; 
} 

輸出:

not const 
const 
1

如果一堆提供只讀訪問數據方面的功能不需要遞增shared_ptr而只需要(const)訪問指向的數據,那麼遵循oop的方法將起作用:

class BaseWrapper { 
public: 
    // ...bunch of functions providing read-only access to aspects of the Data... 
protected: 
    virtual const Data* getData() = 0; 
    virtual ~BaseWrapper(){} 
}; 

class WrapperConst: public BaseWrapper { 
public: 
    WrapperConst(shared_ptr<Data const> data) : myData(data) {} 
protected: 
    const Data* getData() { 
     return myData.get(); 
    }; 
private: 
    shared_ptr<Data const> myData; 
}; 

class WrapperMutable : public BaseWrapper { 
public: 
    WrapperMutable(shared_ptr<Data> data) : myData(data) {} 
    void updateData() { myData->update(); } 
protected: 
    const Data* getData() { 
     return myData.get(); 
    }; 
private: 
    shared_ptr<Data> myData; 
}; 

優點:您不需要shared_ptr的其他副本。

缺點:您現在使用的是您不需要的虛擬功能。只讀函數無權訪問shared_ptr,因此它們可能不會複製它。更多的樣板。

1

我想,你不需要在WrapperMutable一個shared_ptr<>,原始指針會做:

class WrapperMutable : public Wrapper 
{ 
public: 
    WrapperMutable(Data* data): 
    Wrapper{shared_ptr<Data const>{data}}, 
    myMutableData{data} {} 

    // Use myMutableData instead of the const myData 
    void updateData() { myMutableData->update(); } 

private: 
    Data* myMutableData; // non-const pointer to the same Data as in Wrapper 
}; 

至少可以節省您的遞增和遞減引用計數器。

從軟件設計的角度來看,你確定,那WrapperMutable「是」Wrapper?我的感覺說,你在某個地方打破單一責任原則。這是我見過的設計問題的最主要原因之一。

順便說一句:請重新考慮,如果shared_ptr<>確實是你在這裏所需要的。它經常被濫用作爲垃圾收集的替代品。使用它,當你真的想表明共享所有權。首選原始指針爲無所有權,​​爲唯一所有權。原因是,與原始指針和​​相比,shared_ptr<>不是免費的。它需要引用計數器的額外增量和減量,並且通常在您取消引用時增加額外的間接級別。另一方面​​將創建與原始指針相同的代碼,並將正確的位置刪除遍佈整個地方。

1
#include <memory> 
using namespace std; 

class Data { 
public: 
    void update() {} // non-const method which modifies internal state of Data 
}; 


//Basic wrapper 
template <typename D> 
class BaseWrapper { 
    public:  
     BaseWrapper(shared_ptr<D> data) : myData(data) {} 

    protected: 
     shared_ptr<D> myData; 
}; 

template <typename D, bool const = std::is_const<D>::value> 
class Wrapper : public BaseWrapper<D> 
{ 
}; 

//Const-Version 
template <typename D> 
class Wrapper<D, true> : public BaseWrapper<D> 
{ 
public: 
    Wrapper(shared_ptr<D> data) : BaseWrapper(data) {} 
}; 

//Non-Const-Version 
template <typename D> 
class Wrapper<D, false> : public BaseWrapper<D> 
{ 
public: 
    Wrapper(shared_ptr<D> data) : BaseWrapper(data) {} 
    void updateData() { myData->update(); } 
}; 



int main() 
{ 
    Wrapper<Data> a(nullptr); 
    Wrapper<const Data> b(nullptr); 
    a.updateData(); 
    b.updateData();//error C2039: 'updateData': is not a member of 'Wrapper<const Data,true>' 
}