[英]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_t
和int
的常見類型是size_t
。 因此,如果向量為空,則將值(size_t)-1
分配給int
m_value,這是一個超出范圍的轉換,調用實現定義的行為。
為了避免依賴於實現定義的行為,代碼可以是:
m_value(myVec.size() ? (int)myVec.size() + 1 : -1)
現在,這保留了原始代碼具有的另一個問題:當myVec.size() >= INT_MAX
時myVec.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.