C++ 在向量中使用具有常量数据成员的类

C++ 在向量中使用具有常量数据成员的类,c++,vector,constants,variable-assignment,C++,Vector,Constants,Variable Assignment,对于这样一个类: class Foo { const int a; }; class Foo { private: int a; public: int getA() const {return a;} }; 可以把那个类放在向量中吗?当我尝试时,我的编译器告诉我它不能使用默认赋值运算符。我试着自己写,但谷歌搜索告诉我,不可能为一个包含常量数据成员的类编写赋值运算符。我发现的一篇帖子说,“如果你让[数据成员]常量,那就意味着你不想让赋值在

对于这样一个类:

class Foo
{
   const int a;
};
class Foo
{
    private:
        int a;
    public:
        int getA() const {return a;}
};

可以把那个类放在向量中吗?当我尝试时,我的编译器告诉我它不能使用默认赋值运算符。我试着自己写,但谷歌搜索告诉我,不可能为一个包含常量数据成员的类编写赋值运算符。我发现的一篇帖子说,“如果你让[数据成员]常量,那就意味着你不想让赋值在第一时间发生。”这是有道理的。我已经编写了一个包含常量数据成员的类,我从来没有打算对它使用赋值,但显然我需要赋值来将它放入向量中。有没有一种方法可以保持常量的正确性?

使用指针向量
std::vector
。如果您想避免自己清理后的麻烦,请使用。

编辑:我在咖啡休息时间的第一次尝试,
static const int a不适用于OP考虑的用例,初始注释证实了这一点,因此我正在重写并扩展我的答案

大多数时候,当我想使一个类的元素为常量时,它是一个常量,它的值在所有时间和类的所有实例中都是常量。在这种情况下,我使用一个静态常量变量:

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
    的值。(这就是为什么,顺便说一句,编写一个跳过
    a
    副本的自定义
    操作符=()
    可能是一个坏主意。)
  • 访问是不同的–您必须通过getter而不是直接访问成员
实际上,在两者之间进行选择时,我使用只读成员。这样做可能意味着您将能够用另一个对象的值替换一个对象的值,而不会违反任何语义。让我们看看它在你的情况下是如何工作的

考虑网格对象,具有宽度和高度。当您最初创建向量时,假设您使用
vector::reserve()
保留一些初始空间,您的向量将填充初始默认初始化(即空)网格。当您要指定到向量中的特定位置,或将栅格推到向量的末端时,可以将该位置处对象的值替换为具有实际内容的栅格。但是你可以接受这个!如果希望宽度和高度保持恒定的原因是为了确保宽度和高度与栅格对象的其他内容之间的一致性,并且您已经验证了在替换栅格的其他元素之前还是之后替换宽度和高度并不重要,那么这个赋值应该是安全的,因为在赋值结束时,实例的全部内容都将被替换,并且您将回到一致状态。(如果默认赋值缺乏原子性是一个问题,那么您可以通过实现自己的赋值运算符来解决这个问题,该运算符使用复制构造函数和
swap()
操作。)

总之,使用只读getter所获得的是能够使用向量或任何具有值语义的容器中的对象。但是,您需要确保Grid的内部操作(或Grid之友的操作)都不会违反此一致性,因为编译器不会为您锁定宽度和高度。这也适用于默认构造、复制构造和赋值

我已经编写了一个包含常量数据成员的类,我从来没有打算对它使用赋值,但显然我需要赋值来将它放入向量中。有没有一种方法可以保持常量的正确性

您必须询问以下约束是否仍然有效

a = b;
 /* a is now equivalent to b */
如果
a
b
类型为
Foo
(您必须定义“等效”含义的语义!)的约束不成立,那么您就不能将
Foo
放入标准容器中。例如,
auto_ptr
不能放入标准容器中,因为它违反了该要求

如果你可以说你的类型满足了这个约束(例如,如果const成员不以任何方式参与你的对象的值,但无论如何考虑将它作为静态数据成员),那么你可以编写你自己的赋值运算符< /p>

class Foo
{
   const int a;
public:
   Foo &operator=(Foo const& f) {
     /* don't assign to "a" */
     return *this;
   }
};

但要三思。在我看来,您的类型似乎不满足约束条件

我正在考虑将数据成员设为非常量,但为私有,并且只能通过get函数访问,如下所示:

class Foo
{
   const int a;
};
class Foo
{
    private:
        int a;
    public:
        int getA() const {return a;}
};

这和康斯特一样好吗?它有什么缺点吗?

那么你是说,如果我确信我永远不会用我的类型写
a=b
,那么写一个不做任何事情的赋值运算符也没关系?对我来说是有道理的,但是你为什么认为我的类型不满足这个约束-编辑-再想想,我知道你在做什么。我同意我的类型不满足约束条件。@Max:他不是这么说的。他说的是像平常一样做所有的赋值,但是忽略
const
成员的赋值。容器可以自由地执行
a=b
,它没有任何内容