【问题标题】:Make only const copies of a const object只制作 const 对象的 const 副本
【发布时间】:2018-03-21 12:35:59
【问题描述】:

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

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

这可能吗?

【问题讨论】:

  • 进入封装。
  • “有时 b 必须是只读的,有时它是可写的。” 你所有的问题都源于这种奇怪的二元性。我建议为您的课程选择一组语义,而不是两组。
  • 这就是为什么你有像 privatepublic 这样的关键字。然后你就可以编写访问器/修饰符来做任何你想做的事情。
  • @BillKotsias:我同意糟糕/有问题的设计并不少见。你是否改进它当然是你的电话
  • 没问题-祝你好运!

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


【解决方案1】:

代码中的缺陷:即使使用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,您可以选择性地启用/禁用A 的成员函数,具体取决于它们是否需要“可写”b

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

但是我想更加强调这条评论:

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

来自Lightness Races in Orbit

你或许应该考虑拆分你的类,这样你就有一个 CommonAWriteableANonWriteableA 使用的功能(名字很糟糕,但我希望你明白我的意思)。

【讨论】:

  • 你说得对,我没有给出完整的故事。我有这个“设计”,你将 b 作为 const 或 non-const 返回,就像这样:B&amp; Get_b() { return b };const B&amp; Get_b() const { return b; },反正我不是很喜欢!
【解决方案2】:

你可以为堆做:

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

那么你就不会通过你得到的指针意外修改对象了(必须存储在const A *,否则编译器会报错)。

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

【讨论】:

    猜你喜欢
    • 2010-09-13
    • 1970-01-01
    • 2019-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-07
    • 2020-11-10
    • 1970-01-01
    相关资源
    最近更新 更多