2

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

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

这可能吗?

4

2 回答 2

2

您可以为堆执行此操作:

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

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

但是它不适用于基于堆栈的对象,因为返回const A &局部变量是一个相当致命的动作,并且“const 构造函数”还没有被发明(相关:为什么 C++ 没有 const 构造函数?

于 2018-03-21T13:00:55.023 回答
2

代码中的缺陷:即使使用const

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

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

带模板的解决方案

您可以向您的类型添加一个“标志”,而不是(ab)使用const类型限定符,以区分您需要能够进行非常量访问的情况和您没有的情况:

#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来选择性地启用/禁用成员函数。Ab

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

我想更加强调这条评论:

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

来自轨道上的轻盈竞赛

您可能应该考虑拆分您的类,以便您拥有 a和 aCommonA都使用的功能(名称很糟糕,但我希望您理解我的意思)。WriteableANonWriteableA

于 2018-03-21T13:37:44.100 回答