只制作 const 对象的 const 副本
Make only const copies of a const object
我有一个 class 其中包含引用,例如:
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
这可能吗?
你可以为堆做:
static const A *constCopy(const A &a); // and of course implement it somewhere
那么你就不会意外地通过你得到的指针修改对象(必须存储在const A *
中,否则编译器会报错)。
但是它不适用于 stack-based 对象,因为返回 const A &
局部变量是一个相当致命的动作,而且 "const constructor" 还没有被发明出来(相关:Why does C++ not have a const constructor?)
您代码中的缺陷:您的数据不是 "protected" 即使 const
const
类型限定符管理对类型成员函数的访问及其成员的访问。由于您的成员 B & b
是一个引用,因此 const
在这里对您没有多大作用:无论哪种方式,在初始化后都不能更改引用。您如何访问该引用的 target 甚至都没有考虑:
const A a(b);
a.b.non_const_function(); // OOPS, no problem!
带模板的解决方案
而不是 (ab) 使用 const
类型限定符,您可以向您的类型添加一个 "flag",以区分您需要能够具有 non-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
,您可以根据它们是否需要 "writeable" b
有选择地启用/禁用 A
的成员函数。
真正的解决方案:重构您的设计
但是我想更加强调这条评论:
"Sometimes b must be read-only, sometimes it is writeable." All your problems stem from this weird duality. I suggest picking one set of semantics for your class, not two
您可能应该考虑拆分您的 class,这样您就有一个 CommonA
,其功能由 WriteableA
和 NonWriteableA
(名称很糟糕) ,但我希望你明白我的意思)。
我有一个 class 其中包含引用,例如:
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
这可能吗?
你可以为堆做:
static const A *constCopy(const A &a); // and of course implement it somewhere
那么你就不会意外地通过你得到的指针修改对象(必须存储在const A *
中,否则编译器会报错)。
但是它不适用于 stack-based 对象,因为返回 const A &
局部变量是一个相当致命的动作,而且 "const constructor" 还没有被发明出来(相关:Why does C++ not have a const constructor?)
您代码中的缺陷:您的数据不是 "protected" 即使 const
const
类型限定符管理对类型成员函数的访问及其成员的访问。由于您的成员 B & b
是一个引用,因此 const
在这里对您没有多大作用:无论哪种方式,在初始化后都不能更改引用。您如何访问该引用的 target 甚至都没有考虑:
const A a(b);
a.b.non_const_function(); // OOPS, no problem!
带模板的解决方案
而不是 (ab) 使用 const
类型限定符,您可以向您的类型添加一个 "flag",以区分您需要能够具有 non-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
,您可以根据它们是否需要 "writeable" b
有选择地启用/禁用 A
的成员函数。
真正的解决方案:重构您的设计
但是我想更加强调这条评论:
"Sometimes b must be read-only, sometimes it is writeable." All your problems stem from this weird duality. I suggest picking one set of semantics for your class, not two
您可能应该考虑拆分您的 class,这样您就有一个 CommonA
,其功能由 WriteableA
和 NonWriteableA
(名称很糟糕) ,但我希望你明白我的意思)。