2

起初,我的印象是成员变量在 C++ 类中通常应该是 const,除非我希望它的成员函数在内部为每个对象修改这些变量(我通常将变量放入私有 :)。

但我现在发现我可能一直都错了。例如,我不能再分配给这样的对象了。我什至不能做移动分配。

比如说 std::string。你可以这样做:

std::string foo("foo");
foo = std::string("bar");

这必须暗示在内部, std::string 只有非常量成员变量,对吗?

我的想法正确吗?对于 C++ 来说相对较新,它的新思维有点奇怪。const 的目的可能不是我想的那样。

那么拥有 const 成员变量的实际目的是什么?

4

7 回答 7

6

C++ 类中的成员通常不应该是 const 吗?

我建议您忽略“通常”如何声明变量。的出现频率const不应让您决定下一个要声明的变量 is 或 is const

const如果在整个程序执行过程中初始化后不应该更改成员的值,则成员应该是。否则,他们不应该是const。这是数据的语义属性,决定是否创建变量的唯一标准const是变量相对于值变化的性质。

它可以改变,还是保持不变? 在后一种情况下,一定要做到const

这必须暗示在内部, std::string 只有非常量成员变量,对吗?

,这并不意味着它,尽管这可能恰好是std::string.

重要的是,你的类中不仅 const有成员(当然,只要你想离开它并且你的类封装了一些资源),所以那些实际上可以用来判断一个对象是否已经被移动自是可修改的。

当你离开一个物体时,你留下的是一个骨架。移动构造函数或移动赋值运算符需要一种方法来“标记”这个被移动的对象作为骨架。通常,这很自然地源于从对象中“窃取内脏”的过程,即复制一些指针并将它们设置为空 - 这样,析构函数和赋值运算符就不会尝试释放它们。为了将移动对象标记为“僵尸”,显然您需要一些可修改的成员变量。

但是,在您的班级中也有一些const成员并没有错。const是一个重要的修饰符,它使你的程序的语义更清晰,你的程序不太可能破坏它。只要合适(即,只要 A 变量的值在初始化后的整个程序执行过程中都不会改变),请使用它

简单地说,const成员变量不会被移动构造函数或移动赋值运算符修改(事实上,它们不会被任何函数修改);但同样,这并不意味着他们不能在场。

于 2013-02-21T18:17:13.823 回答
2

我的印象是成员变量在 C++ 类中通常应该是 const,除非我希望它的成员函数来修改这些变量

您忘记的是赋值运算符(包括默认运算符)成员函数。例子:

struct Foo {
    const int i = 0;

    Foo& operator =(const Foo& src)
    {
        if (&src == this)
            return *this;
        i = src.i;
        return *this;
    }
};

由于赋值运算符是成员函数,它显然必须能够修改Foo::i. 如果没有,则不能调用它。请记住,给定Foo对象ab,这:

a = b;

实际上是语法糖:

a.operator= (b);

还要记住,在这种情况下,默认赋值运算符会被删除,因为Foo::iis const。这意味着Foo如果您自己不编写赋值运算符,则完全没有赋值运算符。

于 2013-02-21T18:23:23.923 回答
1

const 对象是您不想更改的对象,它们在程序执行期间保持不变。您可以在类的构造函数的成员初始化列表中为它们分配一个值,但不能超出此范围。如果您有一个可以具有不同值的变量,则不应使用 const。

于 2013-02-21T18:16:50.060 回答
1

我首先建议你看看这个相关问题的答案。

快速而肮脏的答案是 const 成员属性包含在对象初始化后保持不变的值。复制操作正在尝试执行导致构造新对象的操作,然后将旧对象的值分配给新对象(在隐式复制构造函数主体中)。作为解决方案,您最可能想要的是创建一个显式复制构造函数,它将原始对象作为参数并初始化初始化列表中的 const 属性值。

复制构造器示例:

ClassWithConstantProperties( const ClassWithConstantProperties &orig) :
    constProperty1(orig.constProperty1)
    constProperty2(orig.constProperty2)
{
}

本教程更全面地解释了 C++ 中隐式定义的类的构造函数,并且在一个优秀的 C++ 参考页面上。

于 2013-02-21T18:29:41.120 回答
1

它取决于对象,但一般来说,当您为对象编写代码时,要对它的不变量负责。我很少使用const或引用成员,但它们在不支持分配的类中是完全可以接受的。

通常,const用作逻辑常量,而不是按位常量。const 更像是你和你的客户之间的合同,而不是保护你免受自己伤害的东西。从这个意义上说,拥有 const 函数、返回 const 引用以及将 const 引用作为参数是很有意义的。然而,参数上的顶级 const 或类中的 const 并没有多大意义。

于 2013-02-21T18:47:52.677 回答
0

类的数据成员描述了该类型对象的状态。const数据成员意味着对象具有某种永不改变的状态。这不是很常见。通常const成员也是static

对数据成员唯一能做的const就是将它初始化为构造函数的成员初始化列表。

std::string您可以从另一个分配给 a 的事实std::string仅意味着它的复制分配运算符不会对其const数据成员做任何事情(因为它当然不能)。

具有const数据成员的类或结构将没有隐式默认的复制赋值运算符。编译器无法分配给您的const数据成员,所以它只是说“如果您想分配,您必须自己定义它。”

于 2013-02-21T18:18:58.307 回答
0

当构造后不应在类内部更改值时,使用常量变量。
一些例子:数字常数(pi,e)和参考。

struct Database_Record
{
    Database_Record(const std::string& table_name)
      : m_table_name(table_name)
    { ; }
    const std::string& get_table_name(void) const
    {  return m_table_name; }
private:
    const std::string m_table_name;
};

在上述表示数据库记录的结构中,它有一个与记录关联的表名,初始化后不能更改;这可以防止记录被写入错误的表。

于 2013-02-21T18:22:09.337 回答