包含shared_ptr的对象的Const正确性

Const correctness with objects containing shared_ptr

本文关键字:对象 正确性 Const ptr shared 包含      更新时间:2023-10-16

考虑对象:

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 nonconst3(const1);
    nonconst3.get() = 3;

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

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

"对我来说这感觉不是const-correct"然而它是:你只是在一个非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

没有,除非你想存储一个shared_ptr<const int>,在这种情况下没有人可以作为非const访问它。

这不会破坏const的正确性。val所指向的整数对象是一个不同的对象,它不被原始对象独占。修改它的值不影响Obj对象的状态

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

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

不,没有……但是,当您可以写入value(在非const getter中)时,您可以使用COW, deep-copy指针。

或者,你可以写两个copy-ctors(对于ref做浅拷贝,对于cref做深拷贝)

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

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

但是,您希望避免对Obj非const实例进行深度复制(这没有问题,并且打算重用旧的共享指针)。为此,必须同时提供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
    //...
}