2010-06-24 29 views

回答

18

這不是必然一個壞主意,但通常,是的。

首先,它是全局數據,全局數據通常是一件壞事。 您擁有的全球化程度越高,您對程序的推理就越難。其次,C++不保證以不同的翻譯單位(.cpp文件)定義的靜態對象的初始化順序 - 所以如果它們相互依賴,則可能會遇到麻煩。

+1

如果代碼會影響在其他文件中聲明的變量,你可能比初始化爲了更大的問題。如果在文件中定義的函數或變量沒有直接用於程序中的其他地方,鏈接器可能會丟棄整個編譯單元,包括初始化代碼。 (這是如果該文件是通過掛鉤或由全球建築安裝註冊表使用的問題。) – 2010-06-24 14:56:48

+0

此外,全局的線程是不安全的,至少可以說和訪問它們是緩慢的(甚至訪問堆更快)。在絕大多數廣闊的案例中,全局都是一個壞主意。當然也有例外。 – Puppy 2010-06-24 21:13:41

+0

構造函數不依賴於順序並進行一些良性的初始化。線程被仔細控制。 – Bruce 2010-06-25 00:35:05

1

除了有問題的形式 - 它不會移植到某些平臺。

1

正如你指出的那樣,它是允許的。在我工作的最後一家公司,當出現這種情況時,我們制定了一項政策,向main()添加一個適當的註釋,以表明它適用於哪些變量。如果你的情況不好,儘量做到最好。

7

是的,這是不好的。由於您無法捕獲異常並處理它們,因此將使用默認處理程序。在C++那意味着調用終止...

例如:內容a.cpp輸出的

#include <stdexcept> 

int f() { throw std::runtime_error("boom"); return 42; } 
int i = f(); 

int main(int argc, char* argv[]) 
{ 
    return 0; 
} 

g++ a.cpp && ./a.out

terminate called after throwing an instance of 'std::runtime_error' 
    what(): boom 
Aborted (core dumped) 

您可以在主試加一個try ... catch,那也不會幫助。

編輯:賈爾夫的觀點也有效。聽聽他的建議。

+0

main()try {...} catch(...){...}怎麼樣? – 2010-06-24 15:02:08

+0

我不認爲這個例子是合法的,因爲'int i = f();'。據我所知,你不能在聲明一個全局變量時調用一個函數。 – 2010-06-24 15:12:01

+0

@Alexandre - 我們正在討論名爲BEFORE主要開始的代碼。因此,main中的try/catch不能達到它,因爲執行此代碼時沒有輸入try。 – 2010-06-24 15:14:08

0

使用具有不平凡的構造函數和析構函數的全局/靜態對象是可怕的。我看到足夠多的巨大軟件項目因爲全局/靜態對象和單例的不受控制的使用而陷入災難。

問題不在於它是在main之外運行的代碼。就是那些物體是以無法控制的順序構建和銷燬的。

另外我認爲這是一個普遍不好的做法,無論如何使用全局變量(即使是普通變量),但有一些例外。 每一段代碼都應該在一個定義良好的上下文中運行,並且所有的變量都應該屬於它。

+0

std :: cout是全球性的。 – ChrisW 2010-06-24 19:15:45

+0

@ChrisW:你不想摧毀std :: cout ...你呢? – smerlin 2010-06-24 22:09:38

+0

@smerlin - 不,但它是一個全局變量的例子;這是構建。 Meyers給出了一個例子(他說std :: cout實現中使用了這個例子):如何控制全局變量的構造順序;或者更確切地說,即使你是全球性的,如果你使用的東西(例如std :: cout)在不同的翻譯單元中,如何保證在使用它之前構建全局。 – ChrisW 2010-06-24 22:32:55

0

它不壞形式在所有,它不是混亂。靜態初始化是該語言的一個有意的特徵。在需要時使用它。與全局相同。在需要時使用它們。像任何功能,知道何時是適當和了解它的侷限性是成爲一個強大的程序員組成部分。

壞形式的重組,否則完全沒有計劃,只是爲了避免全局或靜態初始化。

+0

這種假設你正在處理一個已經寫好的項目;原來的問題實際上是規定性的,而不是評估性的。 – 2010-06-24 19:13:13

0

我會走得這麼遠,說這是不好的形式對非吊艙,特別是如果你在一個團隊中,因爲可以很容易地從這個出現初始化順序問題主要是工作。

// the following invokes undefined behavior. 
static bool success=cout<<"hello world\n"; 

人們通常不會像上面那樣編寫代碼,但要考慮成功是否被初始化爲另一個函數的返回值。現在有人試圖在該函數中使用cout或cerr或任何其他全局對象調用相同的未定義行爲。

對於構造函數和析構函數用戶定義類型,考慮替代的方法,其中訪問初始化:

static Foo& safe_static() 
{ 
    static Foo f; 
    return f; 
} 

不幸的是,這也與線程安全的問題,所以需要建設某種鎖定機構'f',如果你正在同時訪問safe_static。

這就是說,我不認爲一個人應該儘量保持簡單。不幸的是,當涉及到在文件範圍定義的用戶定義的對象時,很容易陷入未定義的行爲。需要額外的努力來寫上面的東西safe_static可以防止很多頭痛。

例外的是另一點。如果你的靜態對象拋出它們的構造函數,那麼你無法捕捉異常。如果你希望你的應用程序非常健壯,甚至可以處理啓動錯誤,那麼你必須仔細地構造代碼(例如:在構造函數中爲你在文件範圍創建的對象設置try/catch塊,以避免引發異常在ctor之外,也避免了拋出的初始化列表)。

如果你在一個團隊中工作,你可能會想:「哦,我沒有訪問我班上的其他全局變量,我不妨在文件範圍內使用一個簡單的全局內部鏈接。 「這可能是真的,但在你知道它之前,你的同事會添加另一個全局變量並嘗試從你的類的構造函數中訪問它。突然之間,你有未定義的行爲,甚至可能不會顯示爲主要平臺上的問題,只是當你試圖將代碼移植到別處時,你的代碼纔會崩潰並做其他奇怪的事情。

這真的不值得的潛在頭痛IMO,以及它是更容易避免,而不是解決問題。

相關問題