【问题标题】:C++ defining a constant member variable inside class constructorC++ 在类构造函数中定义一个常量成员变量
【发布时间】:2023-03-03 00:41:01
【问题描述】:

通常当你的类中有一个常量私有成员变量时,它只有一个 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) 我需要获取另一个对象 - 我将使用本例中的vector——作为构造函数的参数,根据参数对象设置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 作为成员变量对我有什么好处吗?

【问题讨论】:

  • : m_value(!myVec.empty() ? myVec.size() + 1 : -1) ?
  • 为什么一定要const
  • 关于你的更新 - 不,我看不到任何好处......
  • 它是恒定的,因此它永远不会被改变。我将它用作游戏中对象的唯一标识符,并且我希望这些标识符在整个游戏过程中保持不变。我认为让它保持不变是个好主意,但仍然不确定它的好处。
  • 你得到了一些价值。编译器可以假设无论发生什么,重复调用同一对象的 getter 将产生相同的值。但是使用它作为唯一标识符,嗯……这就是指针的用途。

标签: c++ constructor initialization constants


【解决方案1】:

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

// 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() &gt; 0 ? int(myVec.size() + 1) : int(-1))即可在初始化时直接计算值。这看起来像一个例子,所以我给了你一个解决问题的非常通用的方法,即使计算你需要的答案的方法可能非常复杂。

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

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

【讨论】:

  • +1 用于解决一般问题,并且解释清楚。
【解决方案2】:

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

这个建议不好:

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() &gt;= INT_MAX 时的转换超出范围。在健壮的代码中,这个问题也应该得到解决。

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

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

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

【讨论】:

    【解决方案3】:

    您有两个基本选择。一种是使用条件运算符,这对于像您这样的简单条件很好:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 这解决了这个例子非常狭窄的情况下的问题。如果计算初始值设定项的代码远比您能够适应表达式的复杂得多,那该怎么办?
      • @Omnifarious - 查看 Omnifarious 给出的答案。
      • @Omnifarious:然后你写一个小函数助手来完成它
      猜你喜欢
      • 2014-10-12
      • 2016-05-16
      • 2015-05-25
      • 2015-08-05
      • 2018-02-22
      • 1970-01-01
      • 2011-10-31
      • 2014-05-22
      • 1970-01-01
      相关资源
      最近更新 更多