在C++类中,成员通常不应该是常量吗?const成员的实际目的
Should members usually not be const in C++ classes? The actual purpose of const members
起初,我认为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
对象a
和b
,这个:
a = b;
实际上是语法糖:
a.operator= (b);
还要记住,在这种情况下,默认赋值运算符会被删除,因为Foo::i
是const
。这意味着,如果你自己不写赋值运算符,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或引用
- 在运算符重载定义中使用成员函数(const错误)
- 如何在声明为 const 的方法中更改类成员
- 如何定义一个没有重复代码的继承的 const 类成员函数?
- 为什么我不能在返回 const 的布尔函数中为类成员变量赋值?C++
- 在类 (C++) 之外设置 const int 成员变量
- 不允许运算符 const 参数调用 const 成员函数
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 有人可以用"显式运算符 const GUID_t&() const"来解释成员函数的函数吗?
- 无法使用类型 'const char *' 的左值初始化类型 'char *' 的成员子对象
- 在 C++ 中通过引用传递类成员时,Const 不起作用
- 我们可以有一个 setter 成员函数作为从 const 对象引用的 const 吗?
- 静态数据成员:它"const declaration / constexpr definition"起作用?
- 使用 const char* 初始化 const ref 字符串成员时幕后会发生什么
- c++ 是否保证标头初始化的静态 const 成员跨编译单元和库共享单个实例?
- 声明与 const 变量和成员函数相同的标识符
- 有效地初始化 const std::vector 类成员
- 为什么我可以调用一个从const方法更改成员的方法
- 成员函数的"this"参数具有"const"类型,但我的函数实际上不是"const"
- 在C++中,从构造函数中将字符串文本分配给成员const char*变量时会发生什么
- 在类声明中初始化 const 成员变量时在调试模式下出现异常