C++在类构造函数中定义了一个常量成员变量
C++ defining a constant member variable inside class constructor
通常,当你的类中有一个常量私有成员变量,它只有一个getter而没有setter时,它看起来像这样:
// Example.h
class Example {
public:
Example(const int value);
const int getValue() const;
private:
const int m_value;
};
// Example.cpp
#include "Example.h"
Example::Example(const int value)
: m_value(value)
{
}
const int Example::getValue() const
{
return m_value;
}
现在我要做的是,有一个像这样的常量int成员变量,但不是在初始化部分这样定义它:: m_value(value)
,我需要取另一个对象(在本例中我将使用向量)作为构造函数的参数,并根据参数对象设置m_value。在这种情况下,如果向量的大小大于0,我将尝试将其大小设置为+1。这就是我所做的:
Example::Example(std::vector<Example*> myVec)
{
if (myVec.size()) {
m_value = myVec.size() + 1;
}
else {
m_value = -1;
}
}
但我得到了一个错误uninitialized member 'Example::m_value' with 'const' type 'const int'
,如果我在初始化部分初始化m_value,我就会得到错误assignment of read-only data-member 'Example::m_value'
,这对我来说很有意义,我应该得到这些错误,但我怎么能绕过它们呢?
编辑:编辑m_value
的唯一方法是在对象本身内部(因为m_value是私有的)。只有getter会限制我将m_value设置为构造函数中设置的值以外的任何值。将常量int作为成员变量有什么好处吗?
使用计算所需的静态成员函数,并在初始化列表中调用该函数。像这样:
// Example.h
class Example {
public:
Example(const int value);
Example(std::vector<Example*> myVec);
const int getValue() const;
private:
const int m_value;
static int compute_m_value(::std::vector<Example*> &myVec);
};
// Example.cpp
#include "Example.h"
Example::Example(const int value)
: m_value(value)
{
}
Example::Example(std::vector<Example*> myVec)
: m_value(compute_m_value(myVec))
{
}
const int Example::getValue() const
{
return m_value;
}
int Example::compute_m_value(::std::vector<Example*> &myVec)
{
if (myVec.size()) {
return myVec.size() + 1;
}
else {
return -1;
}
}
在这种特殊情况下,函数非常简单,您可以简单地在构造函数中使用三元运算符(也称为: m_value(myVec.size() > 0 ? int(myVec.size() + 1) : int(-1)
)来直接计算初始化时的值。这看起来像是一个例子,所以我给了你一个非常通用的解决问题的方法,即使计算你需要的答案的方法可能非常复杂。
一般的问题是常量成员变量(以及作为BTW引用的成员变量)必须在初始值设定项列表中初始化。但是初始化器可以是表达式,这意味着它们可以调用函数。由于这个初始化代码非常特定于类,所以它应该是类的私有函数(或者可能是受保护的函数)。但是,由于调用它是为了在构造类之前创建一个值,因此它不能依赖于类实例的存在,因此没有this
指针。这意味着它需要是一个静态成员函数。
现在,myVec.size()
的类型是std::vector<Example*>::size_t
,并且该类型是无符号的。而您使用的sentinel值为-1,但事实并非如此。你把它存储在一个int
中,它的大小可能不适合放它。如果你的矢量很小,这可能不是问题。但是,如果向量的大小是基于外部输入的,或者如果你不知道它会有多大,或者任何其他因素,这将成为一个问题。您应该考虑一下这一点,并相应地调整您的代码。
首先,变量是在类定义中定义的,而不是在构造函数中。它在构造函数中被初始化。
其次,实现这一点的方法就像构造函数当前所做的那样:将初始值设定项列表中的值存储在其中:
Example::Example(std::vector<Example*> myVec)
: m_value(myVec.size() ? myVec.size() + 1 : -1) {
}
您有两个基本选项。一种是使用条件运算符,它适用于像您这样的简单条件:
Example::Example(const std::vector<Example*> &myVec)
: m_value( myVec.size() ? myVec.size() + 1 : -1)
{}
对于更复杂的事情,可以将计算委托给成员函数。注意不要在它内部调用虚拟成员函数,因为它将在构造过程中被调用。使其成为static
:是最安全的
class Example
{
Example(const std::vector<Example*> &myVec)
: m_value(initialValue(myVec))
{}
static int initialValue(const std::vector<Example*> &myVec)
{
if (myVec.size()) {
return myVec.size() + 1;
} else {
return -1;
}
}
};
当然,后者也适用于类外定义。我把它们放在课堂上是为了节省空间;打字。
这个答案解决了所有其他答案的问题:
这个建议很糟糕:
m_value(myVec.size() ? myVec.size() + 1 : -1)
条件运算符将其第二个和第三个操作数转换为通用类型,而不考虑最终的选择。
在这种情况下,size_t
和int
的常见类型是size_t
。因此,如果向量为空,则将值(size_t)-1
分配给int
m_value,这是一个超出范围的转换,调用实现定义的行为。
为了避免依赖于实现定义的行为,代码可以是:
m_value(myVec.size() ? (int)myVec.size() + 1 : -1)
现在,这保留了原始代码存在的另一个问题:myVec.size() >= INT_MAX
时的转换超出范围。在健壮代码中,这个问题也应该得到解决。
我个人更喜欢添加一个helper函数的建议,该函数执行此范围测试,并在值超出范围时抛出异常。虽然代码开始变得难以阅读,但一行代码是可能的:
m_value( (myVec.empty() || myVec.size() >= INT_MAX) ? -1 : (int)myVec.size() + 1 )
当然,还有一些其他方法可以更干净地处理这个问题,例如,将size_t
用于m_value
,并将(size_t)-1
作为哨兵值,或者最好完全避免对哨兵值的需要。
- 用C++中的一个变量定义一个常量
- 从另一个常量标准::映射初始化一个常量标准::映射的一部分
- isdigit() 和 isalnum() 给出错误,因为输入是一个常量字符并且无法转换。其他可能查看输入是否为数字的方法?
- C++ 在 none 常量指针函数中返回一个常量指针
- 为什么重载"<<"需要一个常量参数?
- 为什么它是一个常量指针而不是指向常量指针
- 是一个常量句柄真的常量
- 将一个常量cstring附加到我自己的String类对象
- C++在类构造函数中定义了一个常量成员变量
- G++抱怨constexpr函数不是一个常量表达式
- 是一个常量字符串Still Preferrable
- 返回一个常量字符 *
- 将一个常量未签名的长流流式传输到流
- 从自定义 printf 函数返回一个常量字符*
- C++做虚函数总是必须是一个常量
- 我想将一个常量字符串写入包含另一个输入数据块的文件
- 如何计算初始化一个常量数组(制作常量查找表)
- C++:在初始化列表中传递一个常量 *p
- 我可以在 protobuf 中定义一个常量字符串吗?
- 初始化一个常量变量,如果失败则断言(c++)