簡體   English   中英

靜態常量字符串(類成員)

[英]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_initializedmain()之前初始化,所以即使在多線程環境中也不需要鎖。 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。

  • 不要使用 std::string,使用 std::string_view 文字
  • 請注意下面的“constexpr”。 這也是一種“編譯時”機制。
  • 沒有內聯並不意味着重復
  • 為此不需要任何 cpp 文件
  • 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.

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