簡體   English   中英

在 C++ 中定義全局常量

[英]Defining global constant in C++

我想在 C++ 中定義一個常量以在多個源文件中可見。 我可以想象以下方法在頭文件中定義它:

  1. #define GLOBAL_CONST_VAR 0xFF
  2. int GLOBAL_CONST_VAR = 0xFF;
  3. 一些函數返回值(例如int get_GLOBAL_CONST_VAR()
  4. enum { GLOBAL_CONST_VAR = 0xFF; }
  5. const int GLOBAL_CONST_VAR = 0xFF;
  6. extern const int GLOBAL_CONST_VAR; 在一個源文件中const int GLOBAL_CONST_VAR = 0xFF;

選項 (1) - 絕對不是您想使用的選項

選項 (2) - 使用頭文件在每個目標文件中定義變量的實例

選項 (3) - IMO 在大多數情況下過度殺戮

選項 (4) - 在許多情況下可能不好,因為 enum 沒有具體類型(C++0X 將增加定義類型的可能性)

所以在大多數情況下,我需要在(5)和(6)之間進行選擇。 我的問題:

  1. 你更喜歡(5)還是(6)?
  2. 為什么(5)可以,而(2)不行?

絕對使用選項 5 - 它是類型安全的,並允許編譯器優化(不要獲取該變量的地址 :) 此外,如果它在標題中 - 將其粘貼到命名空間中以避免污染全局范圍:

// header.hpp
namespace constants
{
    const int GLOBAL_CONST_VAR = 0xFF;
    // ... other related constants

} // namespace constants

// source.cpp - use it
#include <header.hpp>
int value = constants::GLOBAL_CONST_VAR;

(5) 准確說出你想說的話。 此外,它可以讓編譯器在大部分時間將其優化掉。 (6) 另一方面,不會讓編譯器對其進行優化,因為編譯器不知道您最終是否會更改它。

(5) 比 (6) “更好”,因為它將GLOBAL_CONST_VAR定義為所有翻譯單元中的積分常量表達式 (ICE)。 例如,您將能夠在所有翻譯單元中將其用作數組大小和大小寫標簽。 在 (6) 的情況下, GLOBAL_CONST_VAR將是僅在定義它的翻譯單元中並且僅在定義點之后的 ICE。 在其他翻譯單元中,它不會像 ICE 那樣工作。

但是,請記住,(5)給出GLOBAL_CONST_VAR內部連接,這意味着的“地址身份” GLOBAL_CONST_VAR將在每個翻譯單元不同,即&GLOBAL_CONST_VAR會給你在每個轉換單元不同的指針值。 在大多數用例中,這無關緊要,但如果您需要一個具有一致全局“地址標識”的常量對象,那么您必須使用 (6),犧牲常量的 ICE-ness過程。

此外,當常數的 ICE-ness 不是問題(不是整數類型)並且類型的大小變大(不是標量類型)時,則(6)通常比(5)更好。

(2) 不行,因為 (2) 中的GLOBAL_CONST_VAR默認有外部鏈接。 如果你把它放在頭文件中,你通常會得到GLOBAL_CONST_VAR多個定義,這是一個錯誤。 默認情況下,C++ 中的const對象具有內部鏈接,這就是為什么 (5) 起作用的原因(這就是為什么,正如我上面所說,您在每個翻譯單元中獲得一個單獨的、獨立的GLOBAL_CONST_VAR )。


從 C++17 開始,您可以選擇聲明

inline extern const int GLOBAL_CONST_VAR = 0xFF;

在頭文件中。 這在所有翻譯單元中為您提供了一個 ICE(就像方法(5)),同時維護GLOBAL_CONST_VAR全局地址標識 - 在所有翻譯單元中它將具有相同的地址。

如果您使用 C++11 或更高版本,請嘗試使用編譯時常量:

constexpr int GLOBAL_CONST_VAR{ 0xff };

如果它是一個常量,那么你應該將它標記為一個常量——這就是我認為 2 不好的原因。

編譯器可以使用該值的 const 性質來擴展一些數學運算,以及使用該值的其他操作。

5 和 6 之間的選擇 - 嗯; 5只是讓我感覺更好。

在 6) 中,該值不必要地與其聲明分離。

我通常會有一個或多個這些頭文件,它們只在其中定義常量等,然后沒有其他“聰明”的東西——漂亮的輕量級頭文件,可以很容易地包含在任何地方。

回答你的第二個問題:

(2) 是非法的,因為它違反了單一定義規則。 它在包含它的每個文件中定義GLOBAL_CONST_VAR ,即不止一次。 (5) 是合法的,因為它不受單一定義規則的約束。 每個GLOBAL_CONST_VAR都是一個單獨的定義,位於包含它的那個文件的本地。 當然,所有這些定義共享相同的名稱和值,但它們的地址可能不同。

C++17 inline變量

這個很棒的 C++17 特性允許我們:

主程序

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

不是main.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

編譯並運行:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

GitHub 上游.

另請參閱: 內聯變量如何工作?

內聯變量的 C++ 標准

C++ 標准保證地址是相同的。 C++17 N4659 標准草案10.1.6“內聯說明符”:

6 具有外部鏈接的內聯函數或變量在所有翻譯單元中應具有相同的地址。

cppreference https://en.cppreference.com/w/cpp/language/inline解釋說,如果沒有給出static ,那么它就有外部鏈接。

內聯變量實現

我們可以觀察它是如何實現的:

nm main.o notmain.o

其中包含:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i

man nmu

“u”符號是唯一的全局符號。 這是標准 ELF 符號綁定集的 GNU 擴展。 對於這樣的符號,動態鏈接器將確保在整個過程中只有一個具有此名稱和類型的符號在使用。

所以我們看到有一個專門的 ELF 擴展。

在 GCC 7.4.0、Ubuntu 18.04 上測試。

const int GLOBAL_CONST_VAR = 0xFF;

因為它是一個常數!

#define GLOBAL_CONST_VAR 0xFF // this is C code not C++
int GLOBAL_CONST_VAR = 0xFF; // it is not constant and maybe not compilled
Some function returing the value (e.g. int get_LOBAL_CONST_VAR()) // maybe but exists better desision
enum { LOBAL_CONST_VAR = 0xFF; } // not needed, endeed, for only one constant (enum elms is a simple int, but with secial enumeration)
const int GLOBAL_CONST_VAR = 0xFF; // it is the best
extern const int GLOBAL_CONST_VAR; //some compiller doesn't understand this

這取決於您的要求。 (5) 最適合大多數正常使用,但往往導致每個目標文件中不斷占用存儲空間。 (6) 可以在重要的情況下解決這個問題。

(4) 也是一個不錯的選擇,如果您的優先事項是保證永遠不會分配存儲空間,但它當然只適用於整數常量。

暫無
暫無

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

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