17

通常当你的类中有一个常量私有成员变量时,它只有一个 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

4 回答 4

32

使用静态成员函数计算您需要的结果并在初始化列表中调用该函数。像这样:

// 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)在构造函数中使用三元运算符(aka )即可在初始化时直接计算值。这看起来像一个例子,所以我给了你一个非常通用的解决问题的方法,即使计算你需要的答案的方法可能非常复杂。

一般问题是必须在初始化列表中初始化常量成员变量(以及 BTW 引用的成员变量) 。但是初始化器可以是表达式,这意味着它们可以调用函数。由于此初始化代码非常特定于该类,因此它应该是该类的私有(或可能受保护)函数。但是,由于在构造类之前调用​​它来创建一个值,因此它不能依赖于类实例的存在,因此没有this指针。这意味着它需要是一个静态成员函数。

现在, 的类型myVec.size()std::vector<Example*>::size_t,并且该类型是无符号的。而且您使用的是 -1 的哨兵值,但事实并非如此。而且您将它存储在一个int可能不适合容纳它的尺寸中。如果您的向量很小,这可能不是问题。但是,如果您的向量根据外部输入获取大小,或者如果您不知道它将获得多大,或任何其他因素,这将成为一个问题。您应该考虑这一点并相应地调整您的代码。

于 2012-11-27T16:13:01.327 回答
5

首先,变量是在类定义中定义的,而不是在构造函数中。它在构造函数中初始化

其次,这样做的方法就像您的构造函数当前所做的那样:将初始值设定项列表中的值存储在其中:

Example::Example(std::vector<Example*> myVec)
    : m_value(myVec.size() ? myVec.size() + 1 : -1) {
}
于 2012-11-27T16:13:18.153 回答
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;
    }
  }
};

当然,后者也适用于类外定义。我把它们放在课堂上以节省空间和打字。

于 2012-11-27T16:16:28.467 回答
0

这个答案解决了所有其他答案的问题:

这个建议不好:

m_value(myVec.size() ? myVec.size() + 1 : -1)

无论最终选择如何,条件运算符都将其第二个和第三个操作数变为一个通用类型。

size_t在这种情况下,和的常见类型intsize_t。因此,如果向量为空,则将该值(size_t)-1分配给intm_value,这是一个超出范围的转换,调用实现定义的行为。


为了避免依赖实现定义的行为,代码可以是:

m_value(myVec.size() ? (int)myVec.size() + 1 : -1)

现在,这保留了原始代码存在的另一个问题:超出范围的转换 when myVec.size() >= INT_MAX. 在健壮的代码中,这个问题也应该得到解决。

我个人更喜欢添加一个辅助函数的建议,该函数执行此范围测试并在值超出范围时抛出异常。尽管代码开始变得难以阅读,但单行是可能的:

m_value( (myVec.empty() || myVec.size() >= INT_MAX) ? -1 : (int)myVec.size() + 1 )

当然,还有一些其他方法可以更干净地处理这个问题,例如使用size_tform_value(size_t)-1作为标记值,​​或者最好完全避免需要标记值。

于 2018-05-10T22:29:03.980 回答