程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C++如何禁止全局對象被析構

C++如何禁止全局對象被析構

編輯:關於C語言
 

全局對象無論是在C++中,還是在C(C裡面的全局變量)裡面 ,都是比較難管理而且不提倡過多的使用。一方面它生命周期不好控制 ,另一方面多線程中共享需要額外的消耗。另外,全局變量或者對象的初始化順序不固定,在進程結束的時候全局對象的析構函數會被隱式調用,調用順序也不固定。如果全局對象之間還有引用關系,會使得程序變得更加糟糕。這裡主要講一些技巧如何防止全局對象被析構。

前言

一般情況下,我們都要求所有new出來的對象,在程序結束的時候,都要將其析構掉。但是在某些情況下,我們希望一些很重要的全局對象在程序運行的整個過程中都存在,而且生命周期跟進程相同。在這種情況下,其實是要防止全局對象被析構,即便是進程結束也不要調用其析構函數。例如:WebKit中JavaScriptCore的VM對象。WebKit的bindings裡面,VM對象就是一個跟進程生命周期相同的一個全局變量。任何時候都不允許它被析構掉,如果被析構掉,HTML就無法執行JavaScript了。

全局指針

如果說到全局對象 ,可能大部分人第一時間想到的就是new一個對象,將其指針保存在全局變量裡面。這種做法其實比較危險。new出來的指針放在全局變量裡面,代碼的其他任何地方都可以獲取該指針,如果不小心被其他地方delete掉了呢?另一方面,也不能滿足程序設計要隱藏實現細節的要求。

全局對象

采用直接保存全局對象的方案,可以避免上面提到的被其他地方delete掉。但是有一個缺點,進程結束的時候,該對象會隱式的被調用,也就是還有可能被析構掉。對於某些比較重要的對象,我們希望任何時候都不被析構掉,例如前面說的VM對象。

靜態局部變量

先來看一段代碼:

1 class A {
2 public:
3 A() {};
4 private:
5 A(const A&);
6 A(A&);
7 };
8
9 A& getInstance()
10 {
11 static A* a = new A();
12 A& obj = *a;
13 return obj;
14 }

這段代碼彌補了上面提到的全局指針和靜態全局變量的缺點。避免了指針的直接傳遞,保證了全局對象的單實例。由於將拷貝構造函數聲明為private,使得所有需要使用A對象的地方,都需要聲明為引用,這樣,即便是進程結束,其構造函數也不會被調用。

下面這段代碼摘自WebKit中WTF的StdLibExtras.h

1 // Use these to declare and define a static local variable (static T;)
2 // so that it is leaked so that its destructors are not called at exit.
3 // Using this macro also allows workarounds a compiler bug present in
4 // Apple's version of GCC 4.0.1.
5 #ifndef DEFINE_STATIC_LOCAL
6 #if COMPILER(GCC) && defined(__APPLE_CC__) \
7 && __GNUC__ == 4 \
8 && __GNUC_MINOR__ == 0 \
9 && __GNUC_PATCHLEVEL__ == 1
10 #define DEFINE_STATIC_LOCAL(type, name, arguments) \
11 static type* name##Ptr = new type arguments; \
12 type& name = *name##Ptr
13 #else
14 #define DEFINE_STATIC_LOCAL(type, name, arguments) \
15 static type& name = *new type arguments
16 #endif
17 #endif

WebKit中,有很多對象都是使用DEFINE_STATIC_LOCAL來定義的:

 

Use these to declare and define a static local variable (static T;) so that it is leaked so that its destructors are not called at exit.Using this macro also allows workarounds a compiler bug present in Apple's version of GCC 4.0.1.

全局對象何時被析構?

很多C++編程規范中,全局對象都是禁止使用的,例如Google的C++ code style的Static and Global Variables。原因是因為類的對象作為全局對象的時候,全局對象的析構順序是由全局對象的構造順序決定的,但是全局對象的構造順序又是不確定的,不同的平台,不同的系統,甚至是不同的Build之間,全局對象的構造順序都是不一樣的。但是有一點是可以確定的,全局對象的析構一定是出現在程序的main()結束之後或者exit()函數調用之後。除了上面提到的方法可以禁止全局對象被析構以外,還有一個方法即使quick_exit。程序結束的時候調用quick_exit來替代exit(),這樣,全局對象的析構函數就不會被調用了。

析構全局對象有什麼影響呢?

很多人看了這篇文章,可能都會問:反正進程都結束了,析構或者不析構全局對象都無所謂。其實是有所謂的。特別是在有多個全局對象或者全局對象被多線程引用的場景。問題是,由於全局對象的析構順序不確定,如果全局對象的析構函數中又對其他全局對象有引用,或者,一個全局對象已經析構掉了,但是這個全局對象還在背某個線程引用,這種情況,在程序退出的時候會出現偶現(由於全局對象析構順序不確定,所以不是必現的)的crash的bug,這種問題一旦出現,比較難定位。所以,如果要一定要使用全局變量,那麼這個變量必須是遵循POD(Plain Old Data)。POD要求數據結構或者類不允許自定義自己的析構函數
 

 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved