通过扩展添加可变性

时间:2015-11-10 12:17:54

标签: c++ inheritance const

我有一个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对象的Data - ness,这意味着WrapperMutable无法修改它。

我考虑过将Wrapper更改为接受并存储非const Data,但通常客户端本身只能访问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
};

有更好的方法吗?显然,从WrapperMutable派生Wrapper是我的问题的根源,但我不想在Wrapper中重新实现WrapperMutable的所有方法。

4 个答案:

答案 0 :(得分:1)

继承表示&#34;种类&#34;关系。

constness不是&#34;种类&#34;关系。

对于可变的东西来说,const是一种非常不同的东西。

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 :(得分:1)

如果提供对数据方面的只读访问权限的一堆函数不需要递增get()而只需要($query_builder = DB::table('table1'); // different than $eloquent_builder = Table1Model::select() )访问权限对于指向的数据,然后遵循oop方法将起作用:

shared_ptr

优势:您不需要另一份const

副本

缺点:您现在使用其他方式不需要的虚拟功能。只读功能无法访问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; }; ,因此他们可能无法复制它。更多样板。

答案 2 :(得分:1)

我认为,shared_ptr<>中不需要WrapperMutable,原始指针会执行:

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吗?我的感觉是,你在某个地方违反了单一责任原则。这是我见过的设计问题的最主要原因之一。

BTW。:如果shared_ptr<>确实是您需要的,请重新考虑。它经常被过度使用作为垃圾收集的替代品。当您真的想要说明共享所有权时,请使用它。首选没有所有权的原始指针和unique_ptr<>的唯一所有权。原因是,shared_ptr<>与原始指针和unique_ptr<>形成对比并不是免费的。它会重新获取参考计数器的额外增量和减量,并且当您取消引用它时通常会增加一个间接级别。另一方面,unique_ptr<>将创建与原始指针相同的代码,并且正确地删除所有地方。

答案 3 :(得分:1)

setText()