簡體   English   中英

C ++在類構造函數中定義常量成員變量

[英]C++ defining a constant member variable inside class constructor

通常當你的類中有一個常量私有成員變量時,它只有一個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'的錯誤assignment of read-only data-member 'Example::m_value'這對我來說都是有道理的,我應該得到那些錯誤,但我怎么能繞過它們呢?

編輯:只有我可以編輯m_value是在對象本身內(因為m_value是私有的)。 只有getter會限制我將m_value設置為除了在構造函數中設置的內容以外的任何內容。 將常量int作為成員變量有什么好處?

使用一個靜態成員函數compute得到你需要的結果並在初始化列表中調用該函數。 像這樣:

// 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) )構造函數直接計算初始化時的值。這看起來像一個例子,所以我給你一個解決問題的非常通用的方法,即使計算你需要的答案的方法可能非常復雜。

一般問題是必須在初始化列表中初始化常量成員變量(以及引用過程為BTW的成員變量)。 但是初始化器可以是表達式,這意味着它們可以調用函數。 由於這個初始化代碼非常特定於類,因此它應該是類的私有(或可能是受保護的)函數。 但是,因為在構造類之前調用​​它來創建一個值,它不能依賴於一個類實例存在,因此沒有this指針。 這意味着它需要是一個靜態成員函數。

現在, myVec.size()的類型是std::vector<Example*>::size_t ,該類型是無符號的。 你使用的是-1的哨兵值,但事實並非如此。 並且你將它存儲在一個int ,無論如何它都可能不適合容納它。 如果你的矢量很小,這可能不是問題。 但是,如果您的矢量基於外部輸入獲取大小,或者您不知道它將獲得多大或任何其他因素,這將成為一個問題。 您應該考慮這一點並相應地調整您的代碼。

首先,該變量在類定義中定義的 ,而不是在構造函數中。 它在構造函數初始化

其次,這樣做的方式就像你的構造函數當前所做的那樣:從初始化程序列表中存儲它的值:

Example::Example(std::vector<Example*> myVec)
    : m_value(myVec.size() ? myVec.size() + 1 : -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;
    }
  }
};

當然,后者也適用於課外定義。 我把它們放在課堂上以節省空間和打字。

這個答案解決了所有其他答案的問題:

這個建議很糟糕:

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_MAXmyVec.size() >= INT_MAX范圍轉換。 在健壯的代碼中,這個問題也應該得到解決。

我個人比較喜歡的建議,增加一個輔助函數,它執行這個范圍測試,如果該值超出范圍會拋出異常。 盡管代碼開始變得難以閱讀,但單行代碼是可能的:

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

當然還有一些其他方法可以更干凈地處理這個問題,例如使用size_t作為m_value並且要么(size_t)-1作為sentinel值,要么最好完全避免使用sentinel值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM