使用std::shared_ptr<const T>无法保证共享实例不可变的问题咨询
嗨,我完全懂你遇到的这个坑!你本来想用std::shared_ptr<const Data>来保证共享的Data实例是不可修改的,但结果发现原来的非const智能指针还是能偷偷改里面的值,这确实挺闹心的对吧?
咱们先拆解下问题出在哪:std::shared_ptr<const T>的作用仅仅是限制通过这个指针本身去修改指向的对象,但它管不了其他拥有非const权限的指针。就像你代码里main函数的data变量,它是std::shared_ptr<Data>(非const版本),这个指针从一开始就拥有修改Data实例的权限——哪怕你把它的一份副本转换成const版本传给了DataHolder,原指针的修改权限并没有被收回,所以你后续改data->value的时候,DataHolder里的m_data指向的是同一个实例,自然会看到变化。
那怎么解决这个问题,真正保证共享实例不可变呢?给你几个实用的方案:
从根源上创建const实例:直接用
std::make_shared<const Data>创建对象,这样所有指向它的指针都只能是const版本,根本没机会修改。比如把main里的代码改成这样:int main() { // 直接初始化const的Data实例,后续没法修改 auto data = std::make_shared<const Data>(Data{5}); // data->value = 10; // 这行编译直接报错,因为实例是const的 DataHolder dataHolder(data); dataHolder.printValue(); // 输出5 }固化非const实例为const后销毁原指针:如果需要先构建对象再设置初始值,那做完初始化后,立刻把它转换成const版本,然后把原非const指针清空,确保没有其他能修改的指针留存:
int main() { auto temp_data = std::make_shared<Data>(); temp_data->value = 5; // 转换成const版本 auto const_data = std::const_pointer_cast<const Data>(temp_data); // 销毁原非const指针,切断修改路径 temp_data.reset(); DataHolder dataHolder(const_data); dataHolder.printValue(); // 输出5 // 这里已经没有能修改这个实例的指针了 }封装Data类,从类层面禁止修改:把Data的成员变量设为private,只提供const的访问接口,完全不让外部有直接修改的机会。比如:
struct Data { explicit Data(int v) : value(v) {} int getValue() const { return value; } private: int value; // 私有成员,外部无法直接修改 };这样不管是const还是非const指针,都只能通过
getValue()读取,根本没地方改值,彻底保证不可变。
回到你原来的代码,问题就出在main里保留了shared_ptr<Data>这个“后门”,只要这个指针还在,就能随时修改共享实例。只要把这个后门堵上,std::shared_ptr<const T>才能真正起到保护作用。
备注:内容来源于stack exchange,提问作者SunBlack

