通常当你的类中有一个常量私有成员变量时,它只有一个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作为成员变量有什么好处?
答案 0 :(得分:29)
使用计算所需的静态成员函数,并在初始化列表中调用该函数。像这样:
// 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;
}
}
在这种特殊情况下,函数非常简单,您只需在构造函数中使用三元运算符(aka : m_value(myVec.size() > 0 ? int(myVec.size() + 1) : int(-1)
)直接计算初始化时的值。这看起来就是一个例子,所以我给了你一个解决这个问题的非常通用的方法,即使计算你需要的答案的方法可能非常复杂。
一般问题是常量成员变量(以及引用过于BTW的成员变量)必须在初始化列表中初始化。但是初始化器可以是表达式,这意味着它们可以调用函数。由于这个初始化代码非常特定于类,因此它应该是类的私有(或可能是受保护的)函数。但是,因为在构造类之前调用它来创建一个值,它不能依赖于一个类实例存在,因此没有this
指针。这意味着它需要是一个静态成员函数。
现在,myVec.size()
的类型为std::vector<Example*>::size_t
,该类型为无符号。你使用的是-1的哨兵值,但事实并非如此。并且你将它存储在int
中,无论如何它都可能不适合容纳它。如果你的矢量很小,这可能不是问题。但是,如果您的矢量基于外部输入获取大小,或者您不知道它将获得多大或任何其他因素,这将成为一个问题。您应该考虑这一点并相应地调整代码。
答案 1 :(得分:5)
首先,变量在类定义中定义,而不是在构造函数中。它在构造函数中初始化。
其次,这样做的方式就像你的构造函数当前所做的那样:从初始化列表中存储它的值:
Example::Example(std::vector<Example*> myVec)
: m_value(myVec.size() ? myVec.size() + 1 : -1) {
}
答案 2 :(得分:0)
您有两个基本选项。一种是使用条件运算符,这对于像你这样的简单条件很好:
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;
}
}
};
当然,后者也适用于课外定义。我把它们放在课堂上以节省空间和空间。打字。
答案 3 :(得分:0)
此答案解决了所有其他答案的问题:
这个建议很糟糕:
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
时超出范围转换。在健壮的代码中,这个问题也应该得到解决。
我个人更喜欢添加辅助函数的建议,该函数执行此范围测试并在值超出范围时抛出异常。虽然代码开始变得难以阅读,但单行代码是可能的:
m_value( (myVec.empty() || myVec.size() >= INT_MAX) ? -1 : (int)myVec.size() + 1 )
当然,还有一些其他方法可以更清晰地处理这个问题,例如:将size_t
用于m_value
,并将(size_t)-1
作为哨兵值,或者最好完全避免使用哨兵值。