在C++类中,成员通常不应该是常量吗?const成员的实际目的

Should members usually not be const in C++ classes? The actual purpose of const members

本文关键字:成员 const 常量 类中 C++ 不应该      更新时间:2023-10-16

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

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

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

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

这意味着在内部,std::string只有非常数成员变量,对吗?

我的想法正确吗?作为C++的新手,它的新思维有点奇怪。const的目的可能不是我想象的那样。

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

在C++类中,成员通常不应该是常量吗?

我建议您忽略变量是如何声明为"通常"的。const出现的频率不应该让您决定要声明的下一个变量是否为const

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

它可能会改变,还是应该保持不变 在后一种情况下,一定要使其成为const

这意味着在内部,std::string只有非常数成员变量,对吗?

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

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

当你从一个对象中移动时,你只剩下一个骨架。move构造函数或move赋值运算符需要一种方法来"标记"从对象中移动的对象作为骨架。通常,这自然源于"窃取移出对象的内脏"的过程,即复制一些指针并将其设置为null——这样,析构函数和赋值运算符就不会试图释放它们。为了将移出的对象标记为"僵尸",显然需要一些可修改的成员变量。

然而,在您的类中有一些const成员并没有错。const是一个重要的修饰符,它使程序的语义更清晰,并且程序不太可能破坏它。只要适当(即初始化后在程序执行过程中变量的值不会改变),就使用它

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

我的印象是,在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::iconst。这意味着,如果你自己不写赋值运算符,Foo就完全没有赋值运算符。

它取决于对象,但通常,当您编写对象的代码,负责其不变量。我很少使用const或参考成员,但它们是在不支持赋值的类中完全可以接受。

通常,const用作逻辑常量,而不是按位常量。const更像是你和你的客户之间的合同,而不是保护你免受伤害的东西。在这个sense,具有const函数,返回const引用,以及将const引用作为参数非常有意义。顶部参数的级别const,或类中的const,实际上并不是然而,意义重大。

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

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

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

复制生成器示例:

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

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

当值在构造后不应在类内更改时,将使用常量变量
一些例子:数值常数(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;
};

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

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

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

事实上,您可以从另一个std::string分配给std::string,这只意味着它的副本分配运算符不会对它的const数据成员做任何事情(因为它当然不能)。

具有const数据成员的类或结构将不会具有隐式默认的复制赋值运算符。编译器不能分配给const数据成员,所以它只说"如果你想分配,你必须自己定义它。"

C++核心指南(C++核心指南基本上是一套由语言的创建者/维护者提供的关于C++编码的久经考验的指南、规则和最佳实践)说,不要让数据成员const或引用你提到的问题,这样类就变得不可复制分配

C.12:不要使数据成员const或引用

相关文章: