鉴于这样的课程:
class Foo
{
const int a;
};
是否可以将该类放入向量中?当我尝试时,我的编译器告诉我它不能使用默认赋值运算符。我试着写自己的,但谷歌搜索告诉我,为const数据成员的类编写赋值运算符是不可能的。我发现的一篇文章说“如果你使[数据成员] const意味着你不想让任务首先发生。”这是有道理的。我已经用const数据成员编写了一个类,我从来没有打算在其上使用赋值,但显然我需要赋值将它放在向量中。有没有办法保持const-correctness?
答案 0 :(得分:8)
我已经用const数据成员编写了一个类,我从不打算在其上使用赋值,但显然我需要赋值将它放在一个向量中。有没有办法保持const-correctness?
您必须询问以下约束是否仍然存在
a = b;
/* a is now equivalent to b */
如果a
和b
类型为Foo
,则此约束不正确(您必须定义“等效”含义的语义!),那么您就是无法放置Foo
到标准容器中。例如,auto_ptr
无法放入标准容器中,因为它违反了该要求。
如果你可以说你的类型它满足这个约束(例如,如果const成员不以任何方式参与你的对象的值,但是然后考虑使它成为静态数据成员),那么你可以编写自己的赋值运算符
class Foo
{
const int a;
public:
Foo &operator=(Foo const& f) {
/* don't assign to "a" */
return *this;
}
};
但请三思!。对我而言,您的类型不会满足约束条件!
答案 1 :(得分:3)
使用指针向量std::vector<Foo *>
。如果您想避免自己清理后的麻烦,请使用boost::ptr_vector
。
答案 2 :(得分:1)
编辑:我的咖啡休息期间我的初始刺,static const int a;
不适用于OP考虑的用例,初步评论确认,所以我改写并扩大我的答案。
大多数情况下,当我想要使一个类的元素成为常量时,它是一个常量,其值在所有时间和所有类的实例中都是常量。在这种情况下,我使用一个静态const变量:
class Foo
{
public:
static const int a;
};
那些不需要在实例之间复制,因此如果应用,那将解决您的分配问题。不幸的是,OP表示这不适用于OP的情况。
如果要创建客户端无法修改的只读值,可以将其设为私有成员变量,并仅通过const getter方法公开它,作为另一个帖子线程表示:
class Foo
{
public:
int get_a() const { return a; }
private:
int a;
};
这和
之间的区别class Foo
{
public:
const int a;
};
是:
const int
可以确保在对象的生命周期内,即使类的实现也不能使用a
的值。这意味着正确的赋值将不起作用,因为在创建对象之后,这将尝试修改a
的值。 (这就是为什么,顺便说一句,编写一个跳过operator=()
副本的自定义a
可能是一个设计明智的坏主意。)实际上,在两者之间进行选择时,我使用只读成员。这样做可能意味着您将能够使用另一个对象的值替换对象的值而不会违反语义。让我们看看它在你的情况下是如何工作的。
考虑您的网格对象,宽度和高度。当您最初创建向量时,假设您使用vector::reserve()
保留了一些初始空间,您的向量将填充初始默认初始化(即空)网格。当您指定向量中的特定位置,或将网格推到向量的末尾时,可以使用具有实际内容的网格替换该位置处的对象的值。但你可能会对此感到满意!如果您希望宽度和高度保持不变的原因是为了确保宽度和高度之间的一致性以及Grid对象的其余内容,并且您已经验证了无论是否相关在替换Grid的其他元素之前或之后替换宽度和高度,然后此赋值应该是安全的,因为在赋值结束时,实例的整个内容将被替换,并且您将返回到一致状态。 (如果默认赋值缺乏原子性是一个问题,你可以通过实现自己的赋值运算符来解决这个问题,该运算符使用了复制构造函数和swap()
运算。)
总之,使用只读getter获得的是能够使用向量或具有值语义的任何容器中的对象。但是,确保Grid的内部操作(或Grid的朋友的操作)都不会违反此一致性,因此编译器不会为您锁定宽度和高度。这适用于默认构造,复制构造和分配。
答案 3 :(得分:0)
我正在考虑将数据成员设为非const,但是私有且只能通过get函数访问,如下所示:
class Foo
{
private:
int a;
public:
int getA() const {return a;}
};
这和const一样好吗?它有什么缺点吗?