C++类中的成员通常不应该是常量吗?常量成员的实际用途

s4n0splo  于 2023-02-06  发布在  其他
关注(0)|答案(8)|浏览(162)

起初,我的印象是,在C++类中,成员变量通常应该是常量,除非我希望它的成员函数在内部为每个对象修改这些变量(这些变量通常放在private:中)。
但是我现在发现我可能沿着都是错的。例如,我不能再给这样的对象赋值了。我甚至不能移动赋值。
比如说std::string,你可以这样做:

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

这意味着std::string内部只有非常数的成员变量,对吗?
我的想法是正确的吗?作为C++的新手,它的新想法有点奇怪。const的用途可能不是我所想的那样。
那么使用常量成员变量的实际目的是什么呢?

wko9yo5t

wko9yo5t1#

C++类中的成员通常不应该是常量吗?
我建议你不要理会变量是如何被声明为“* 通常 *"的。const出现的 * 频率 * 并不是让你决定你要声明的 * 下一个 * 变量是否是const的东西。
如果成员的值在初始化后的整个程序执行过程中不应更改,则成员应为const。否则,成员不应为const。这是数据的语义属性,在决定是否将变量设为const时,应使用的唯一标准是变量在值更改方面的性质。

  • 它可以改变,还是应该保持不变?*在后一种情况下,一定要使它成为const

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

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

重要的是,类中并不是只有const成员(当然,只要你想从它移动,并且你的类封装了一些资源),所以那些实际上可以用来告诉对象是否已经被移动的成员是可以修改的。
当你从一个对象中移出时,你剩下的就是一个 backbone 。move构造函数或move赋值运算符需要一种方法来“标记”这个被移出的对象为一个 backbone 。通常,这是从被移出的对象的“窃取内脏”过程中自然得出的,也就是复制一些指针并将它们设置为null --这样,析构函数和赋值操作符不会试图释放它们。2为了将移出的对象标记为“僵尸”,显然你需要一些可修改的成员变量。
然而,在类中 also 有一些const成员并没有什么错。const是一个重要的修饰符,它使程序的语义更加清晰,并且程序不太可能破坏它。只要合适(即只要A变量的值在初始化后的整个程序执行过程中不会改变),就使用它
简单地说,const成员变量不会被move构造函数或move赋值运算符修改(事实上,它们不会被任何函数修改);但这并不意味着它们不能存在。

cig3rfwq

cig3rfwq2#

我的印象是在C++类中成员变量通常应该是常量,除非我希望它的成员函数修改这些变量
你忘记了赋值运算符,包括默认的,* 是一个成员函数。

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就完全没有赋值运算符。

nukf8bse

nukf8bse3#

这取决于对象,但一般来说,当你为一个对象编写代码时,* 你 * 负责它的不变量。我很少使用const或引用成员,但它们在不支持赋值的类中是完全可以接受的。
一般来说,const被用作逻辑const,而不是位const。const更多的是你和你的客户之间的一个契约,而不是保护你自己。在这个意义上,拥有const函数,返回const引用,并将const引用作为参数是很有意义的。然而,参数上的顶级const,或者类中的const,实际上并没有多大意义。

cgh8pdjw

cgh8pdjw4#

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

k4aesqcs

k4aesqcs5#

首先,我建议您查看this related question的答案。
快速而肮脏的答案是常量成员属性包含的值在对象初始化后保持不变。复制操作试图执行一个动作,该动作导致构造一个新对象,然后将旧对象的值赋给新对象(在隐式复制构造函数体中)。您最可能希望的解决方案是创建一个显式复制构造函数,它将原始对象作为参数,并初始化初始化列表中的常量属性值。
复制构造函数的示例:

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

This tutorial更全面地解释了C中类的隐式定义构造函数,也在优秀的C参考页面上。

u91tlkcl

u91tlkcl6#

常量变量用于构造后不应在类内部更改值的情况。
一些例子:数字常数(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;
};

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

jm81lzqq

jm81lzqq7#

类的数据成员描述了该类型对象的状态。一个const数据成员意味着对象有一些永远不会改变的状态。这并不常见。通常const成员也是static
对于const数据成员,你能做的唯一事情就是将它初始化为构造函数的成员初始化列表。
您可以从一个std::string赋值给另一个std::string,这一事实仅仅意味着它的复制赋值操作符不对它的const数据成员做任何事情(因为它当然不能)。
包含const数据成员的类或结构体将不会有隐式默认的复制赋值运算符。编译器不能赋值给您的const数据成员,所以它只是说“如果您想要赋值,您必须自己定义它。”

flvtvl50

flvtvl508#

C核心指南(C核心指南基本上是一套关于 C++ 的创建者/维护者编写代码的经过验证的指南、规则和最佳实践)指出,不要让数据成员常量化或引用,因为正是您提到的问题,类变得不可复制赋值。
C.12:不要使数据成员成为常量或引用

相关问题