仅创建 const 对象的 const 副本

Make only const copies of a const object

提问人:Bill Kotsias 提问时间:3/21/2018 更新时间:3/22/2018 访问量:100

问:

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

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

这可能吗?

C++ 常量 按引用传递 default-copy-constructor

评论

3赞 Passer By 3/21/2018
输入 encapsulation。
3赞 Lightness Races in Orbit 3/21/2018
“有时 b 必须是只读的,有时它是可写的。”你所有的问题都源于这种奇怪的二元性。我建议为你的类选择一组语义,而不是两组。
0赞 Benjamin Barrois 3/21/2018
这就是为什么你有像私人公共这样的关键词。然后,您可以编写可以执行任何操作的访问器/修饰符。
1赞 Lightness Races in Orbit 3/21/2018
@BillKotsias:我同意糟糕/有问题的设计并不少见。你是否改进它当然是你的决定
1赞 Lightness Races in Orbit 3/21/2018
没问题,祝你好运!

答:

2赞 tevemadar 3/21/2018 #1

您可以为堆执行此操作:

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

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

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

2赞 Daniel Jour 3/21/2018 #2

代码中的缺陷:即使有const

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

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_ifAb

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

我想进一步强调这条评论:

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

来自 Lightness Races in Orbit

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

评论

0赞 Bill Kotsias 3/22/2018
你是对的,我没有给出完整的故事。我有这个“设计”,您将 b 返回为 const 或 non-const,如下所示:,反正我不是很喜欢!B& Get_b() { return b };const B& Get_b() const { return b; }