在分配对象时保持恒定性

Preserve constness on assigning an object

本文关键字:恒定性 分配 对象      更新时间:2023-10-16

分配时是否可以保持对象的恒定性?考虑一个保存指向动态内存的指针的类,作业时需要软拷贝。(有一些更复杂的逻辑,包括引用计数,但它与问题无关)。

class Vec {
private:
    int *data_; // dynamic array
public:
    int * Data() { return data_;} // access for modification
    const int * Data() const { return data_;} // read only access
    Vec subVec(int pos) { // create writable submat
        Vec ret; 
        ret.data_ = &data_[pos];
        return ret;
    }
    const Vec subVec(int pos) const { // create read only submat
        Vec ret; 
        ret.data_ = &data_[pos];
        return ret;
    }
};
void processVec(const Vec & src) {
    src.Data()[0] = 1; // not allowed, since src const
    Vec subVec = src.subVec(2); // call const version of subVec and create
                                // a soft copy - wrapper for a part of array.
                                // Assignment creates a copy, which removes constness
    subVec.Data()[0] = 1; // allowed! Since subVec is a copy, but modifies 
                          // identical dynamic memory from the wrapped src!
}

我希望subVec.Data()[0] = 1;失败,因为它应该保持恒定。

你的问题是,你正在定义一个指向某个对象的智能指针,但你混淆了智能指针的恒常性(类似于Foo* const bar;)和对象的恒常性(类似于const Foo* bar;)。您需要的是将指针的常常性与对象的恒常性分开。

通过使用两个智能指针类而不是一个,可以相对轻松地做到这一点:一个基类,它为const对象实现智能指针,另一个派生类,它为非const对象实现智能指针。

有了它,你总是可以将你的智能指针降级到"const"基类,制作无拷贝的"const"副本等。您甚至可以向派生的非"const"智能指针类添加一个构造函数,该类采用"const"基类的智能指针来创建数据的深层副本。

我会通过在

处理 const 或非 const 对象时创建不同的返回类型来模仿iteratorconst_iterator习语。这里有一个为常量调用subVec实现不同类型的示例:

class Vec {
 // ...
public:
 // ...
    const int * Data() const { return data_;} // read only access
    Vec subVec(int pos) { // create writable submat
        Vec ret; 
        ret.data_ = &data_[pos];
        return ret;
    }
    class SubVec
    {
    private:
        const int* data_;
    public:
        SubVec(const int* data) : data_(data) {}
        const int * Data() const { return data_;} // read only access
    };
    const SubVec subVec(int pos) const { // create read only submat
        SubVec ret(&data_[pos]);        
        return ret;
    }    
};

这将在 const 情况下生成您想要的错误,并将在非 const 情况下进行编译。使用 auto 可以更轻松地编写代码。

IIUC,常量或非常量元素是在运行时分配的,因此类型系统(静态)无法帮助您。您需要为每个元素存储一些布尔标志,并对赋值执行运行时检查以查看它是否允许。