【问题标题】:Const correctness with objects containing shared_ptr包含 shared_ptr 的对象的 const 正确性
【发布时间】:2013-04-24 02:28:19
【问题描述】:

考虑对象:

class Obj
{
    public:
        Obj() : val(new int(1)) {}
        int& get() {return *val;}
        const int& get() const {return *val;}

    private:
        std::shared_ptr<int> val;
};

正如预期的那样,当构建对象并制作副本时,它们都可以通过 Obj 暴露的 shared_ptr 修改相同的值。

    Obj nonconst1;
    Obj nonconst2(nonconst1);
    nonconst2.get() = 2;
    cout << nonconst1.get() << ", " << nonconst2.get() << endl;

也可以从非 const 之一复制构造 const Obj 对象,这似乎是正确的,因为它允许读取但不能写入该值 - 正如预期的那样,以下代码会导致编译错误:

    const Obj const1(nonconst1);
    const1.get() = 3;

但是,可以从 const 复制构造一个非 const Obj,然后允许修改该值。

    Obj nonconst3(const1);
    nonconst3.get() = 3;

对我来说,这感觉不正确。

有没有办法防止这种行为,同时仍然允许复制构造函数工作?在我的实际用例中,我仍然希望 Obj 的 std 容器成为可能。

【问题讨论】:

  • 也许,你应该定义复制构造函数采用非常量引用以避免复制const对象?
  • @Nawaz,这确实有效(我没有意识到可以在复制构造函数中使用非常量引用)。不幸的是,我也希望 const Obj 能够复制构造其他 Const obj 但尝试 Obj(const Obj&amp; o) const 不会编译

标签: c++ constants shared-ptr


【解决方案1】:

“对我来说,这感觉不正确”但它是:您只是在非 const Obj 上调用非 const get 方法。没有错。

如果你真的需要你所追求的行为,你可以使用像 Obj 这样的 const 代理,但你的客户当然必须能够处理它:

class Obj
{
  //...
  //original class definition goes here
  //...
  friend class ConstObj;
};  

class ConstObj
{
  public:
    ConstObj( const Obj& v ) : val( v.val ) {}
    const int& get() const { return *val; }

   private:
    std::shared_ptr<int> val;
};

//usage:
class WorkingWithObj
{
public:
  WorkingWithObj();
  Obj DoSomethingYieldingNonConstObj();
  ConstObj DoSomethingYieldingConstObj();
};

WorkingWithObj w;
Obj nonconst( w.DoSomethingYieldingNonConstObj() );
nonconst.get() = 3;

ConstObj veryconst( nonconst );
veryconst.get() = 3; //compiler error

ConstObj alsoconst( w.DoSomethingYieldingConstObj() );
alsoconst.get() = 3; //compiler error

【讨论】:

  • 你能解释一下你的答案吗?我在Obj 和使用模式中没有看到任何内容?
  • @Nawaz 添加了一些 cmets / 使用示例。虽然我不得不说我可能永远不会在实际代码中使用这样的结构..
【解决方案2】:

不,没有,除非您想存储 shared_ptr&lt;const int&gt;,在这种情况下 没有人 可以作为非常量访问它。

【讨论】:

    【解决方案3】:

    这不会破坏 const 的正确性。 val 指向的整型对象是一个不同的对象,它不属于原始对象。修改其值不会影响Obj 对象的状态。

    【讨论】:

      【解决方案4】:

      有没有办法防止这种行为,同时仍然允许复制构造函数工作?在我的实际用例中,我仍然希望 Obj 的 std 容器成为可能。

      您可以指定不同的复制构造函数来从 const 对象复制 - 这意味着您可以例如避免复制共享指针,而是使用 NULL 指针创建非常量对象,或者您可以对指向的数字进行深度复制。不过,我会非常谨慎地做这种事情——根据复制变量的常量来获得不同的行为是很奇怪的——我担心这会让你很难推理你的程序行为。但是,您必须选择一些行为或接受当前行为,因为 std::vector&lt;&gt; 有时会创建副本 - 您不能简单地将其保留为未定义。

      【讨论】:

        【解决方案5】:

        不,没有...但是您可以使用 COWdeep-copy 指针,当您可以写入 value(在非常量 getter 中)时。

        或者,你可以写两个copy-ctorsref做浅拷贝,cref做深拷贝)。

           A(A& obj) : pointer(obj.pointer) {}
           A(const A& obj) : pointer(new int(*obj.pointer)) {}
        

        【讨论】:

          【解决方案6】:

          手动实现Obj 的复制构造函数,然后应该复制共享指针的内容。这样可以避免通过nonconst3 修改const1 的内容,因为它们指向不同的int 实例。

          但是,您希望避免对Obj非常量 实例进行深度复制(这没问题,并且打算重用旧的共享指针)。为此,您必须同时提供 const 和非 const 复制构造函数,并且仅在 const 中复制:

          class Obj
          {
            public:
              //...
              Obj(Obj &o) : val(o.val) {}                            // not a deep copy
              Obj(const Obj &o) : val(std::make_shared(o.get())) {}  // deep copy
              //...
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-05-06
            • 1970-01-01
            • 2023-02-02
            • 2018-03-06
            • 2014-05-12
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多