C ++在类构造函数中定义常量成员变量

时间:2012-11-27 16:10:00

标签: c++ constructor initialization constants

通常当你的类中有一个常量私有成员变量时,它只有一个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作为成员变量有什么好处?

4 个答案:

答案 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_tint的常见类型为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作为哨兵值,或者最好完全避免使用哨兵值。