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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13588306/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:30:57  来源:igfitidea点击:

C++ defining a constant member variable inside class constructor

c++constructorinitializationconstants

提问by Omnifarious

Usually when you have a constant private member variable in your class, which only has a getter but no setter, it would look something like this:

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

Now what I'm trying to do, is have a constant int member variable like that, but instead of defining it in the initializing section like so: : m_value(value)I need to take an other object - I'll use a vector in this example - as the constructor's parameter, and set m_value based on the parameter object. In this case, I'll try to do vector's size + 1, if the size is above 0. So this is what I did:

现在我想做的是有一个像这样的常量 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;
    }
}

But I get an error uninitialized member 'Example::m_value' with 'const' type 'const int'and if I init m_value inside the initializing section, I get the error assignment of read-only data-member 'Example::m_value'which all makes sense to me, I'm supposed to get those errors, but how could I go around them?

但是我收到一个错误uninitialized member 'Example::m_value' with 'const' type 'const int',如果我在初始化部分 init m_value ,我得到的错误assignment of read-only data-member 'Example::m_value'对我来说都是有意义的,我应该得到这些错误,但是我怎么能绕过它们呢?

Edit:Only way I could edit m_valueis inside the object itself (since m_value is private). Having only getter would limit me from setting m_value to anything other than what it's set in the constructor. Do I benefit anything from having constant int as a member variable?

编辑:我可以编辑的唯一方法m_value是在对象本身内部(因为 m_value 是私有的)。只有 getter 会限制我将 m_value 设置为除构造函数中设置的值以外的任何值。将常量 int 作为成员变量有什么好处吗?

回答by Omnifarious

Use a static member function the compute to result you need and call that function in the initialization list. Like this:

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

// 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;
    }
}

In this particular case, the function is so very simple you can simply use the ternary operator (aka : m_value(myVec.size() > 0 ? int(myVec.size() + 1) : int(-1)) in the constructor to directly compute the value at initialization. This looked like an example, so I gave you a very general method of solving the problem, even when the method of computing the answer you need might be very complex.

在这种特殊情况下,该函数非常简单,您可以简单地: m_value(myVec.size() > 0 ? int(myVec.size() + 1) : int(-1)在构造函数中使用三元运算符(又名)来直接计算初始化时的值。这看起来像一个例子,所以我给了你一个非常通用的解决问题的方法,即使计算你需要的答案的方法可能非常复杂。

The general issue is that constant member variables (and member variables that are references too BTW) mustbe initialized in the initializer list. But initializers can be expressions, which means they can call functions. Since this initialization code is pretty specific to the class, it should be a function private (or maybe protected) to the class. But, since it's called to create a value before the class is constructed it can't depend on a class instance to exist, hence no thispointer. That means it needs to be a static member function.

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

Now, the type of myVec.size()is std::vector<Example*>::size_t, and that type is unsigned. And you're using a sentinel value of -1, which isn't. And you're storing it in an intwhich may not be the right size to hold it anyway. If your vector is small, this likely isn't an issue. But if your vector acquires a size based on external input, or if you don't know how large it will get, or any number of other factors, this will become an issue. You should be thinking about that and adjusting your code accordingly.

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

回答by Pete Becker

First, the variable is definedin the class definition, not in the constructor. It's initializedin the constructor.

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

Second, the way to do that is just like what your constructor currently does: store the value in it from the initializer list:

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

Example::Example(std::vector<Example*> myVec)
    : m_value(myVec.size() ? myVec.size() + 1 : -1) {
}

回答by Angew is no longer proud of SO

You have two basic options. One is to use the conditional operator, which is fine for simple conditions like yours:

您有两个基本选项。一种是使用条件运算符,它适用于像您这样的简单条件:

Example::Example(const std::vector<Example*> &myVec)
  : m_value( myVec.size() ? myVec.size() + 1 : -1)
{}

For more complex things, you can delegate the computation to a member function. Be careful not to call virtual member functions inside it, as it will be called during construction. It's safest to make it static:

对于更复杂的事情,您可以将计算委托给成员函数。注意不要在其中调用虚成员函数,因为它会在构造过程中被调用。最安全的做法是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;
    }
  }
};

The latter works with out-of-class definitions as well, of course. I've placed them in-class to conserve space & typing.

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

回答by M.M

This answer addresses a problem with all of the other answers:

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

This suggestion is bad:

这个建议不好:

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

The conditional operator brings its second and third operand to a common type, regardless of the ultimate selection.

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

In this case the common type of size_tand intis size_t. So if the vector is empty, the value (size_t)-1is assigned to the intm_value, which is an out-of-range conversion, invoking implementation-defined behaviour.

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



To avoid relying on implementation-defined behaviour the code could be:

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

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

Now, this retains another problem that the original code had: out of range conversion when myVec.size() >= INT_MAX. In robust code this problem should also be addressed.

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

I would personally prefer the suggestion to add a helper function, which performs this range test and throws an exception if the value is out of range. The one-liner is possible although the code is starting to get hard to read:

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

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


Of course there are a few other ways to deal with this problem more cleanly, e.g. use size_tfor m_valueand either have (size_t)-1as the sentinel value, or preferably avoid the need for a sentinel value entirely.

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