[英]Static constant string (class member)
我想要一個類的私有靜態常量(在本例中是一個形狀工廠)。
我想要那種東西。
class A {
private:
static const string RECTANGLE = "rectangle";
}
不幸的是,我從 C++ (g++) 編譯器中得到了各種錯誤,例如:
ISO C++ 禁止初始化成員“RECTANGLE”
非整數類型“std::string”的靜態數據成員的類內初始化無效
錯誤:使“矩形”靜態
這告訴我這種成員設計不符合標准。 您如何在不必使用#define 指令的情況下擁有私有文字常量(或者可能是公共的)(我想避免數據全局性的丑陋!)
任何幫助表示贊賞。
您必須在類定義之外定義靜態成員並在那里提供初始化程序。
第一的
// In a header file (if it is in a header file in your case)
class A {
private:
static const string RECTANGLE;
};
接着
// In one of the implementation files
const string A::RECTANGLE = "rectangle";
您最初嘗試使用的語法(類定義中的初始化程序)僅允許用於整數和枚舉類型。
從 C++17 開始,您有另一個選項,它與您的原始聲明非常相似:內聯變量
// In a header file (if it is in a header file in your case)
class A {
private:
inline static const string RECTANGLE = "rectangle";
};
不需要額外的定義。
在 C++11 中,您現在可以執行以下操作:
class A {
private:
static constexpr const char* STRING = "some useful string constant";
};
在類定義中,您只能聲明靜態成員。 它們必須在類之外定義。 對於編譯時整數常量,標准規定您可以“初始化”成員。 不過,這仍然不是一個定義。 例如,如果沒有定義,獲取地址是行不通的。
我想提一下,我看不到使用 std::string 而不是 const char[] for constants的好處。 std::string 很好,但它需要動態初始化。 所以,如果你寫類似
const std::string foo = "hello";
在命名空間范圍內,foo 的構造函數將在 main 開始執行之前運行,並且此構造函數將在堆內存中創建常量“hello”的副本。 除非你真的需要 RECTANGLE 成為一個 std::string 你也可以寫
// class definition with incomplete static member could be in a header file
class A {
static const char RECTANGLE[];
};
// this needs to be placed in a single translation unit only
const char A::RECTANGLE[] = "rectangle";
那里! 沒有堆分配,沒有復制,沒有動態初始化。
干杯,S。
在 C++ 17 中,您可以使用內聯變量:
class A {
private:
static inline const std::string my_string = "some useful string constant";
};
請注意,這與abyss.7 的答案不同:這個定義了一個實際的std::string
對象,而不是const char*
這只是額外的信息,但如果您真的想要頭文件中的字符串,請嘗試以下操作:
class foo
{
public:
static const std::string& RECTANGLE(void)
{
static const std::string str = "rectangle";
return str;
}
};
雖然我懷疑這是推薦的。
類靜態變量可以在標頭中聲明,但必須在 .cpp 文件中定義。 這是因為靜態變量只能有一個實例,編譯器無法決定將它放在哪個生成的目標文件中,因此您必須做出決定。
為了在 C++11 中的聲明中保留靜態值的定義,可以使用嵌套的靜態結構。 在這種情況下,靜態成員是一個結構,必須在 .cpp 文件中定義,但值在標頭中。
class A
{
private:
static struct _Shapes {
const std::string RECTANGLE {"rectangle"};
const std::string CIRCLE {"circle"};
} shape;
};
整個靜態結構不是初始化單個成員,而是在 .cpp 中初始化:
A::_Shapes A::shape;
這些值通過以下方式訪問
A::shape.RECTANGLE;
或 -- 因為成員是私有的,並且只能從 A 中使用 -- 與
shape.RECTANGLE;
請注意,此解決方案仍然存在靜態變量初始化順序的問題。 當使用靜態值初始化另一個靜態變量時,第一個靜態變量可能尚未初始化。
// file.h
class File {
public:
static struct _Extensions {
const std::string h{ ".h" };
const std::string hpp{ ".hpp" };
const std::string c{ ".c" };
const std::string cpp{ ".cpp" };
} extension;
};
// file.cpp
File::_Extensions File::extension;
// module.cpp
static std::set<std::string> headers{ File::extension.h, File::extension.hpp };
在這種情況下,靜態變量頭將包含 { "" } 或 { ".h", ".hpp" },具體取決於鏈接器創建的初始化順序。
正如@abyss.7 所述,如果可以在編譯時計算變量的值,您也可以使用constexpr
。 但是如果你用static constexpr const char*
聲明你的字符串並且你的程序使用std::string
否則會有開銷,因為每次使用這樣的常量時都會創建一個新的std::string
對象:
class A {
public:
static constexpr const char* STRING = "some value";
};
void foo(const std::string& bar);
int main() {
foo(A::STRING); // a new std::string is constructed and destroyed.
}
要使用該類內初始化語法,常量必須是由常量表達式初始化的整型或枚舉類型的靜態常量。
這是限制。 因此,在這種情況下,您需要在類之外定義變量。 參考@AndreyT的答案
您可以選擇上面提到的const char*
解決方案,但是如果您一直需要字符串,那么您將有很多開銷。
另一方面,靜態字符串需要動態初始化,因此如果你想在另一個全局/靜態變量的初始化過程中使用它的值,你可能會遇到初始化順序的問題。 為避免這種情況,最便宜的方法是通過 getter 訪問靜態字符串對象,它會檢查您的對象是否已初始化。
//in a header
class A{
static string s;
public:
static string getS();
};
//in implementation
string A::s;
namespace{
bool init_A_s(){
A::s = string("foo");
return true;
}
bool A_s_initialized = init_A_s();
}
string A::getS(){
if (!A_s_initialized)
A_s_initialized = init_A_s();
return s;
}
請記住只使用A::getS()
。 因為任何線程只能由main()
啟動,並且A_s_initialized
在main()
之前初始化,所以即使在多線程環境中也不需要鎖。 A_s_initialized
默認為 0(在動態初始化之前),因此如果在 s 初始化之前使用getS()
,則可以安全地調用 init 函數。
順便說一句,在上面的答案中:“ static const std::string RECTANGLE() const ”,靜態函數不能是const
,因為如果有任何對象(沒有 this 指針),它們就不能改變狀態。
可能只是這樣做:
static const std::string RECTANGLE() const {
return "rectangle";
}
或者
#define RECTANGLE "rectangle"
當前標准僅允許對靜態常量整數類型進行此類初始化。 因此,您需要按照 AndreyT 的解釋進行操作。 但是,這將在下一個標准中通過新的成員初始化語法提供。
快進到 2018 年和 C++17。
static_assert 僅在編譯時“有效”
using namespace std::literals; namespace STANDARD { constexpr inline auto compiletime_static_string_view_constant() { // make and return string view literal // will stay the same for the whole application lifetime // will exhibit standard and expected interface // will be usable at both // runtime and compile time // by value semantics implemented for you auto when_needed_ = "compile time"sv; return when_needed_ ; }
};
以上是一個適當且合法的標准 C++ 公民。 它可以很容易地參與任何和所有 std:: 算法、容器、實用程序等。 例如:
// test the resilience
auto return_by_val = []() {
auto return_by_val = []() {
auto return_by_val = []() {
auto return_by_val = []() {
return STANDARD::compiletime_static_string_view_constant();
};
return return_by_val();
};
return return_by_val();
};
return return_by_val();
};
// actually a run time
_ASSERTE(return_by_val() == "compile time");
// compile time
static_assert(
STANDARD::compiletime_static_string_view_constant()
== "compile time"
);
享受標准 C++
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.