仅制作const对象的const副本

时间:2018-03-21 12:35:59

标签: c++ const pass-by-reference copy-constructor default-copy-constructor

我有一个包含引用的类,如:

class A {
 A(B &b) : b(b) {} // constructor
 B &b;
}

有时b必须是只读的,有时它是可写的。当我创建一个const A a(b);对象时,很明显我想保护其中的数据const。 但是 - 偶然 - 很容易制作一个非常量的对象副本,这将使其中的数据容易受到攻击。

const A a(b); // b object protected here
A a_non_const(a);
a_non_const.b.non_const_function(...); // b not protected now

我认为我应该以某种方式阻止对象的副本const,如下所示:

const A a(b);
const A a2(a); // OK!
A a_non_const(a); // Compiler error

这有可能吗?

2 个答案:

答案 0 :(得分:2)

您可以为堆执行此操作:

static const A *constCopy(const A &a); // and of course implement it somewhere

然后你不会意外地通过你得到的指针修改对象(必须存储在const A *中,否则编译器会抱怨)。

然而,它不适用于基于堆栈的对象,因为返回局部变量的const A &是一个相当致命的动作,而“const构造函数”尚未发明(相关:Why does C++ not have a const constructor?

答案 1 :(得分:2)

代码中的

缺陷:即使使用const

,您的数据也不会受到“保护”

const类型限定符管理对类型的成员函数的访问以及对其成员的访问。由于您的成员B & b是引用,const在这里对您没有太大作用:无论是初始化之后,都无法更改引用。如何访问该引用的目标甚至不被考虑:

const A a(b);
a.b.non_const_function(); // OOPS, no problem!

带模板的解决方案

除了(ab)使用const类型限定符,您可以为您的类型添加“标记”,以区分您需要能够进行非常量访问的情况和您不能进行的情况T:

#include <type_traits>

struct B {
    void danger() {
    }
    void all_fine() const {
    }
};

template<bool Writeable>
struct A {
    using BRef = typename std::conditional<Writeable, B &, B const &>::type;
    BRef b;

    A (BRef b) : b(b) {};
};

using ConstA = A<false>;
using NonConstA = A<true>;

int main() {
    B b;
    ConstA a(b);
    //NonConstA nc_a(a);
    ConstA another_a(a);
    //another_a.b.danger();
    another_a.b.all_fine();

    NonConstA a2(b);
    a2.b.danger();
}

对于某些std::enable_if,您可以选择性地启用/禁用A的成员函数,具体取决于它们是否需要“可写”b

真正的解决方案:重构您的设计

但是我想更多地强调这个评论:

  

“有时b必须是只读的,有时候它是可写的。”你所有的问题都源于这种奇怪的二元性。我建议为你的班级选择一套语义,而不是两个

     

来自Lightness Races in Orbit

您可能应该考虑拆分您的班级,以便CommonAWriteableA使用NonWriteableA功能{名称很糟糕,但我希望您理解我的意思。)