Chapter 18. 特殊工具與技術(shù)
18.1 優(yōu)化內(nèi)存分配Optimizing Memory Allocation
分配內(nèi)存和創(chuàng)建對象是兩個不同的概念。分配內(nèi)存,并不一定會立即創(chuàng)建對象。同樣,內(nèi)存釋放和對象撤銷也是兩個不同的概念。
18.1.1. C++ 中的內(nèi)存分配
new操作:給指定的類型分配內(nèi)存并在新分配的內(nèi)存上構(gòu)造指定類型的對象。
在未構(gòu)造的內(nèi)存上給對象賦值,而不是初始化這個對象,這種賦值是未定義的。對很多類來說,這樣做會導致運行期崩潰。賦值包括刪除現(xiàn)有的對象,如果不存在現(xiàn)有對象,那么這個賦值操作就會導致災難性的結(jié)果。Assigning to an object in unconstructed memory rather than initializing it is undefined. For many classes, doing so causes a crash at run time. Assignment involves obliterating the existing object. If there is no existing object, then the actions in the assignment operator can have disastrous effects.
C++提供兩種方法分配和釋放未構(gòu)造的內(nèi)存:
- allocator類
- new和delete操作符
C++提供兩種方法創(chuàng)建和銷毀對象:
- allocator類
- new操作符
- 直接調(diào)用對象的析構(gòu)函數(shù)(但是此時只是把對象占用的內(nèi)存變成了為構(gòu)造內(nèi)存,但是這個內(nèi)存并沒有釋放哦)
- uninitialized_fill and uninitialized_copy,是拷貝不是賦值
18.1.2. allocator 類
這是一個模板類,提供類型化的內(nèi)存分配,對象創(chuàng)建和撤銷。它把內(nèi)存分配和對象創(chuàng)建這兩件事情分開來。當一個allocator對象分配內(nèi)存時間,它只是根據(jù)給定的類型分配出空間,這個空間的大小可以用來保存給定類型的對象。但是這時,所分配的空間還沒有構(gòu)建。這就相當于預先分配preallocation。
allocator<T> a;
|
定義allocator對象a,按類型T分配內(nèi)存。
|
a.allocate(n)
|
分配未構(gòu)造內(nèi)存,這個內(nèi)存的大小是n個類型T的對象
|
使用allocator管理類成員數(shù)據(jù)
這里是用vector類來舉例說明。從vector的工作機制中可以看到:如果沒有空閑的單元,vector重新分配內(nèi)存。這樣獲得新的空間后,vector拷貝當前現(xiàn)有的對象到新的內(nèi)存空間,然后釋放舊的內(nèi)存空間。If there isn't a free element, then the vector is reallocated: The vector obtains new space, copies the existing elements into that space, adds the new element, and frees the old space.因此從這個工作機理中可以看出vector的效率是比較差的。
下面的這段代碼很好的說明了alloctor是如何管理類成員數(shù)據(jù)的。
- allocator分配內(nèi)存空間一定是和類型相關(guān)的。因此在Vector里面定義了靜態(tài)成員alloc。
static std::allocator<T> alloc; // object to get raw memory
|
- alloc.allocate()返回值是一個指定類型的指針。
- 先調(diào)用析構(gòu)函數(shù)alloc.destroy(),再釋放空間alloc.deallocate()。
template <class T> void Vector<T>::reallocate()
{
std::ptrdiff_t size = first_free - elements;
std::ptrdiff_t newcapacity = 2 * max(size, 1);
// allocate space to hold newcapacity number of elements of type T
T* newelements = alloc.allocate(newcapacity);
// construct copies of the existing elements in the new space
uninitialized_copy(elements, first_free, newelements);
// destroy the old elements in reverse order
for (T *p = first_free; p != elements; /* empty */ )
alloc.destroy(--p);
// deallocate cannot be called on a 0 pointer
if (elements)
// return the memory that held the elements
alloc.deallocate(elements, end - elements);
// make our data structure point to the new elements
elements = newelements;
first_free = elements + size;
end = elements + newcapacity;
}
|
18.1.3. operator new 函數(shù)和 operator delete 函數(shù)
operator new和operator delete和標準庫中的其它操作符有些不同,它們是不能重載的。
operator new和operator delete有兩種重載版本:
void *operator new(size_t); // allocate an object
void *operator new[](size_t); // allocate an array
void *operator delete(void*); // free an object
void *operator delete[](void*); // free an array
|
可以用operator new和operator delete模擬allocator:
T* newelements = alloc.allocate(newcapacity);
改編成
T* newelements = static_cast<T*>(operator new[](newcapacity * sizeof(T)));
alloc.deallocate(elements, end - elements);
改編成
operator delete[](elements);
一般而言,使用 allocator 比直接使用 operator new 和 operator delete 函數(shù)更為類型安全。In general, it is more type-safe to use an allocator rather than using the operator new and operator delete functions directly.
18.1.4. 定位 new 表達式Placement new Expressions
定位new表達式實際上就是要實現(xiàn)在已經(jīng)分配的內(nèi)存空間上構(gòu)造對象的功能。
基本形式:
new (place_address) type
new (place_address) type (initializer-list)
對比construct
alloc.construct(first_free, t);
等價于:
new (first_free) T(t);
· 定位new表達式更加靈活。通過下面的Example可以看出二者之間的區(qū)別:
allocator<string> alloc;
string *sp = alloc.allocate(2); // allocate space to hold 2 strings
// two ways to construct a string from a pair of iterators
new (sp) string(b, e); // construct directly in place
alloc.construct(sp + 1, string(b, e)); // build and copy a temporary
|
1. 當定位new表達式初始化一個對象時,它使用構(gòu)造函數(shù),直接創(chuàng)建對象。When placement new initializes an object, it can use any constructor, and builds the object directly.
2. alloc.construct()函數(shù)總是使用拷貝構(gòu)造函數(shù)The construct function always uses the copy constructor.
· 從性能角度考慮,alloc.construct()函數(shù)總是要構(gòu)造臨時對象然后再拷貝它。
· 另外有些類是不支持copy構(gòu)造函數(shù)的,這種情況下就只能用定位new了。
18.1.5. 顯式析構(gòu)函數(shù)的調(diào)用(Explicit Destructor Invocation)
既然可以通過定位new調(diào)用構(gòu)造函數(shù),那么對應于此,還可以通過顯示調(diào)用析構(gòu)函數(shù)來取消對象。
調(diào)用操作符delete不會執(zhí)行析構(gòu)函數(shù),而只是釋放所指向的內(nèi)存。Calling the operator delete function does not run the destructor; it only frees the indicated memory.
18.1.6. 類特定的 new 和 delete(Class Specific new and delete)
這部分要說明的是類如何優(yōu)化new表達式的行為(optimizing the behavior of new expressions.)。
這件事的理論依據(jù)是,類通過定義自己的成員,操作符new和delete,就可以管理其對象使用的內(nèi)存。A class may manage the memory used for objects of its type by defining its own members named operator new and operator delete.
當編譯器看到類類型的new或delete表達式的時候,它先看這個類是不是定義了操作符new和delete成員。如果類定義自己的這兩個操作符函數(shù),編譯器就直接調(diào)用這些函數(shù)分配和釋放對象的內(nèi)存。否則,編譯器調(diào)用標準庫里的new和delete版本。When the compiler sees a new or delete expression for a class type, it looks to see if the class has a member operator new or operator delete. If the class defines (or inherits) its own member new and delete functions, then those functions are used to allocate and free the memory for the object. Otherwise, the standard library versions of these functions are called.
new和delete成員函數(shù)
重載new和delete包括一些限定條件:
- 首先必須是成對出現(xiàn),就是說如果重載new,也必須重載delete。If a class defines either of these members, it should define both of them.
- 必須定義為靜態(tài)函數(shù)static。
L需要復習下下virtual析構(gòu)函數(shù),不virtual那又怎么樣?
當一個基類指針指向的是一個派生類的對象時,如果析構(gòu)函數(shù)是virtual的,那么編譯器在運行期動態(tài)綁定到底調(diào)用哪個類的析構(gòu)函數(shù)。但是如果析構(gòu)函數(shù)沒有聲明為virtual,編譯器在編譯期就確定調(diào)用基類的析構(gòu)函數(shù),這就是靜態(tài)綁定。
new
返回值類型是void*,形參類型是size_t。
delete
返回值類型是void,形參類型是一個參數(shù)的void*,或者是兩個參數(shù)的void*和size_t。
不得不說這部分講的太簡單了,只好從Google上再找些資料學習哈。
定義:
#include <malloc.h>
struct base
{
base()
{
throw int(3);
}
~base() {}
void* operator new( size_t nsize, const char*,int)
{
void* p = malloc( nsize );
return p;
}
void operator delete( void *p)
{
free(p);
}
void operator delete( void* p,const char*,int)
{
free( p );
}
};
|
調(diào)用:
nt main( void )
{
base* p = null;
try
{
p = new base;
delete p;
}
catch(...)
{
}
return 0;
}
|
數(shù)組操作符new[]和delete[]
覆蓋類特定的內(nèi)存分配(Overriding Class-Specific Memory Allocation)
如何強制調(diào)用全局的操作符new和delete:
Type *p = ::new Type; // uses global operator new
::delete p; // uses global operator delete
|
18.1.7. 一個內(nèi)存分配器基類(A Memory-Allocator Base Class)
這個部分就是這一節(jié)所講內(nèi)容的一個完整的實例。
CachedObj可以看做是一種freelist。
freelist是啥?Google了一下下,KAO,原來有N多種freelist的實現(xiàn)。這是一種內(nèi)存管理技術(shù),包括預先分配沒有構(gòu)造對象的內(nèi)存,對象只在需要時在這些內(nèi)存上創(chuàng)建。當對象釋放時,它們所占用的內(nèi)存返回給預先分配的內(nèi)存而不是直接返回給系統(tǒng)。Memory management technique that involves preallocating unconstructed memory to hold objects that will be created as needed. When objects are freed, their memory is put back on the free list rather than being returned to the system.
定義代碼:
template <class T> class CachedObj {
public:
void *operator new(std::size_t);
void operator delete(void *, std::size_t);
virtual ~CachedObj() { }
protected:
T *next;
private:
//add_to_freelist的作用就是想freelist中添加對象
static void add_to_freelist(T*);
//靜態(tài)成員負責管理freelist,對于每種類型只需要一個freelist。
static std::allocator<T> alloc_mem;
//freestor就是指向freelist表頭的指針。
static T *freeStore;
//chunk是一次預分配內(nèi)存空間的大小。
static const std::size_t chunk;
};
|
使用CachedObj - 派生類定義
(1):
class Screen: public CachedObj<Screen> {
// interface and implementation members of class Screen are unchanged
};
|
(2):
template <class Type>
class QueueItem: public CachedObj< QueueItem<Type> > {
// remainder of class declaration and all member definitions unchanged
};
|
分配(Allocation)如何工作
new表達式:QueueItem<Type> *pt = new QueueItem<Type>(val);
- 使用QueueItem<T>::operator new從freelist為對象分配空間。
- 在分配的空間上,使用類型T的copy構(gòu)造函數(shù)構(gòu)建對象。
delete:delete pt;
- 執(zhí)行QueueItem的析構(gòu)函數(shù)。
- 把對象所占用的內(nèi)存返還給freelist。
定義操作符new
功能:從freelist里獲得一個對象的內(nèi)存。
template <class T>
void *CachedObj<T>::operator new(size_t sz)
{
// 鏈表中的數(shù)據(jù)類型必須一致
if (sz != sizeof(T))
throw std::runtime_error
("CachedObj: wrong size object in operator new");
if (!freeStore) {
// 鏈表為空,分配新的內(nèi)存
T * array = alloc_mem.allocate(chunk);
// 把新分配的內(nèi)存單元追加到freelist,freelist是個鏈表,所以把數(shù)組轉(zhuǎn)換成鏈表,這樣每個對象的next指針都指向下一個對象
for (size_t i = 0; i != chunk; ++i)
add_to_freelist(&array[i]);
}
// freestore總是指向下一個有效的單元
T *p = freeStore;
freeStore = freeStore->CachedObj<T>::next;
return p; // constructor of T will construct the T part of the object
}
|
定義delete操作符
功能:就是要把對象占用的內(nèi)存還給freelist。
template <class T> void CachedObj<T>::operator delete(void *p, size_t)
{
if (p != 0)
// put the "deleted" object back at head of freelist
add_to_freelist(static_cast<T*>(p));
}
|
add_to_freelist成員
template <class T> void CachedObj<T>::add_to_freelist(T *p)
{
//這是一個小技巧,為的是避免調(diào)用派生類的next成員(如果存在)
p->CachedObj<T>::next = freeStore;
freeStore = p;
}
|
18.2 運行期類型識別Run-Time Type Identification
通過兩個操作符提供RTTI:
- typeid:返回指針或者引用指向的對象的實際類型。
- dynamic_cast:把指向基類的對象的指針或者引用轉(zhuǎn)換成派生類的指針或引用。
對于具有虛函數(shù)的類,RTTI在運行期執(zhí)行;對于其它的類型是在編譯期執(zhí)行的。The RTTI operators execute at run time for classes with virtual functions, but are evaluated at compile time for all other types.
dynamic_cast:動態(tài)強制類型轉(zhuǎn)換
使用動態(tài)強制類型轉(zhuǎn)換要小心。在任何可能的情況下,定義和使用虛函數(shù)比直接接管類型管理好得多。Dynamic casts should be used with caution. Whenever possible, it is much better to define and use a virtual function rather than to take over managing the types directly.
18.2.1. dynamic_cast 操作符
dynamic_cast操作符實際上執(zhí)行了兩種操作:
- 驗證要執(zhí)行的強制類型轉(zhuǎn)換是不是有效。It begins by verifying that the requested cast is valid.
- 只有當強制類型轉(zhuǎn)換有效時,操作符才執(zhí)行實際的轉(zhuǎn)換操作。Only if the cast is valid does the operator actually do the cast.
使用dynamic_cast 操作符
大師給出了dynamic_cast操作符的使用方法,并例舉出這樣做的三大好處,總而言之就是盡量把代碼出錯的幾率降到最小。
if (Derived *derivedPtr = dynamic_cast<Derived*>(basePtr))
{
// use the Derived object to which derivedPtr points
} else { // BasePtr points at a Base object
// use the Base object to which basePtr points
}
|
在條件語句中使用dynamic_cast 操作符保證強制轉(zhuǎn)換以及轉(zhuǎn)換結(jié)果測試在一個表達式中。Performing a dynamic_cast in a condition ensures that the cast and test of its result are done in a single expression.
這看上去很簡單,但是很重要,因為這樣可以降低程序出錯的概率。
使用 dynamic_cast 和引用類型
對于引用類型的強制轉(zhuǎn)換,和指針的略有不同,這是因為引用不能是空null。
void f(const Base &b)
{
try {
const Derived &d = dynamic_cast<const Derived&>(b);
// use the Derived object to which b referred
} catch (bad_cast) {
// handle the fact that the cast failed
}
}
|
18.2.2. typeid 操作符
當typeid操作符的操作數(shù)不是類類型或者是類類型但是不包含虛函數(shù),typeid操作符指定的是操作數(shù)的靜態(tài)類型。當操作數(shù)是定義了至少一個虛函數(shù)的類類型時,類型是在運行期計算出來的。When the operand is not of class type or is a class without virtual functions, then the typeid operator indicates the static type of the operand. When the operand has a class-type that defines at least one virtual function, then the type is evaluated at run time.
使用typeid操作符
Base *bp;
Derived *dp;
// compare type at run time of two objects
if (typeid(*bp) == typeid(*dp)) {
// bp and dp point to objects of the same type
}
|
// test whether run time type is a specific type
if (typeid(*bp) == typeid(Derived)) {
// bp actually points to a Derived
}
|
18.2.3. RTTI 的使用
這是一個RTTI使用的簡單的例子:
問題提出:如何定義基類、派生類的相等操作符
相等的含義是:
(1) 類型相同
(2) 指定數(shù)據(jù)成員的值相同
愚蠢的方法是針對每個派生類的組合都去重載操作符“==”。至于多愚蠢想象一下就夠了哦J
18.2.4. type_info 類
- 這個類和編譯器相關(guān),不同的編譯器對這個類的定義可能存在差異
- type_info類的構(gòu)造函數(shù)和拷貝各種哦函數(shù)都是私有的private,唯一獲得type_info對象的方法就是通過typeid操作符。
- 成員函數(shù)name的返回值依賴于編譯器。但是對于同一個類型,它的返回值是唯一的。這句話的含義是代碼中是不能出現(xiàn)if(typeid(obj).name()==”string”)這樣的代碼,因為不同的編譯器name函數(shù)的返回值是不一樣的。
std::string obj;
if(typeid(obj).name()==”string”) //Error:
if(typeid(obj)== typeid(std::string) ) //OK
|
18.3 類成員指針Pointer to Class Member
定義
指向數(shù)據(jù)成員的指針
string Screen::*ps_Screen = &Screen::contents;
|
指向成員函數(shù)的指針
char (Screen::*pmf)() const = &Screen::get;
|
pmf是一個指向Screen類的無形參的get函數(shù)的指針。
Screen中get函數(shù)的定義:
char get() const;
char get(index ht, index wd) const;
|
帶形參的定義:
char (Screen::*pmf2)(Screen::index, Screen::index) const;
pmf2 = &Screen::get;
|
使用typedef為成員指針定義別名
// Action is a type name
typedef char (Screen::*Action)(Screen::index, Screen::index) const;
|
這樣上面的定義就可以簡化成:
Action get = &Screen::get;
|
使用類成員指針
類成員指針說來說去的,其實還是指針,是指針,就對應有解引用操作(*)和箭頭操作(->)。
使用指向成員函數(shù)的指針
// pmf points to the Screen get member that takes no arguments
char (Screen::*pmf)() const = &Screen::get;
Screen myScreen;
char c1 = myScreen.get(); // call get on myScreen
char c2 = (myScreen.*pmf)(); // equivalent call to get
Screen *pScreen = &myScreen;
c1 = pScreen->get(); // call get on object to which pScreen points
c2 = (pScreen->*pmf)(); // equivalent call to get
|
使用指向數(shù)據(jù)成員的指針
Screen::index Screen::*pindex = &Screen::width;
Screen myScreen;
// equivalent ways to fetch width member of myScreen
Screen::index ind1 = myScreen.width; // directly
Screen::index ind2 = myScreen.*pindex; // dereference to get width
Screen *pScreen;
// equivalent ways to fetch width member of *pScreen
ind1 = pScreen->width; // directly
ind2 = pScreen->*pindex; // dereference pindex to get width
|
應用
成員指針的一個具體的應用就是成員指針函數(shù)列表(Pointer-to-Member Function Tables)。說來也很簡單,就是把函數(shù)指針保存成Array,根據(jù)下標來索引調(diào)用哪一個函數(shù)。因為這些函數(shù)被定義成了類成員函數(shù),這就用到了成員函數(shù)指針。這也算是一種典型的應用了。
18.4 內(nèi)嵌類Nested Classes
這內(nèi)嵌類實際上是在它的外圍類enclosing class里定義了一種新的類型成員。A nested class defines a type member in its enclosing class.
哦。。。這讓我想到的是Java中的內(nèi)部類,這兩個東西是不是類似L
18.4.1 實現(xiàn)內(nèi)嵌類
定義內(nèi)嵌類
template <class Type> class Queue {
// interface functions to Queue are unchanged
private:
// public members are ok: QueueItem is a private member of Queue
// 只有Queue和它的友元可以訪問QueueItem
struct QueueItem {
QueueItem(const Type &);
Type item; // value stored in this element
QueueItem *next; // pointer to next element in the Queue
};
QueueItem *head; // pointer to first element in Queue
QueueItem *tail; // pointer to last element in Queue
};
|
定義內(nèi)嵌類成員
在哪里定義?
- 必須和外圍類的定義在同一個作用域里。
- 如果內(nèi)嵌類的成員在自身類的外面定義,那么她是不能定義在外圍類里的。很好理解哦,內(nèi)嵌類的成員不是外圍類的成員嘛。
下面這個例子(噢噢,不是花生),就是定義QueueItem的構(gòu)造函數(shù):
// defines the QueueItem constructor
// for class QueueItem nested inside class Queue<Type>
//這是QueueItem的構(gòu)造函數(shù)定義它是內(nèi)嵌在Queue<Type>作用域的
template <class Type>
Queue<Type>::QueueItem::QueueItem(const Type &t): item(t), next(0) { }
|
在外圍類之外定義內(nèi)嵌類
定義Queue類,只要前向聲明QueueItem是內(nèi)嵌類。
template <class Type> class Queue {
// interface functions to Queue are unchanged
private:
struct QueueItem; // forward declaration of nested type QueueItem
QueueItem *head; // pointer to first element in Queue
QueueItem *tail; // pointer to last element in Queue
};
|
在另一個文件中定義QueueItem,注意一定要用限定struct Queue<Type>
template <class Type>
struct Queue<Type>::QueueItem {
QueueItem(const Type &t): item(t), next(0) { }
Type item; // value stored in this element
QueueItem *next; // pointer to next element in the Queue
};
|
內(nèi)嵌模板實例化
當外圍類模板實例化時,內(nèi)嵌類是不會自動實例化的。A nested class of a class template is not instantiated automatically when the enclosing class template is instantiated.內(nèi)嵌類只有在上下文中用到時才實例化。就是說:Queue<int> qi;只是實例化了Queue<int>而沒有實例化 QueueItem<int>。
18.4.2 內(nèi)嵌類作用域的名字查找
當處理類成員聲明時,。當處理定義時,完整的內(nèi)嵌類或者外圍類必須在同一作用域里。When processing the declarations of the class members, any name used must appear prior to its use. When processing definitions, the entire nested and enclosing class(es) are in scope.
18.5 聯(lián)合Union
定義Union:
union TokenValue {
char cval;
int ival;
double dval;
};
|
限制條件:
- 不能包含有靜態(tài)成員。
- 不能包含引用。
- 不能包含那些具有構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值操作符的類類型的對象。
- 不能包含有虛函數(shù)。
使用
定義
// 初始化TokenValue,但是只能為第一個成員使用初始化式
TokenValue first_token = {'a'};
// 未初始化TokenValue對象
TokenValue last_token;
// 定義指向TokenValue對象的指針
TokenValue *pt = new TokenValue;
|
使用
last_token.cval = 'z';
pt->ival = 42;
|
嵌套Union
class Token {
public:
// indicates which kind of value is in val
enum TokenKind {INT, CHAR, DBL};
TokenKind tok;
union { // unnamed union
char cval;
int ival;
double dval;
} val; // member val is a union of the 3 listed types
};
|
匿名Union
因為匿名的Union沒有提供訪問其成員的方法,因此Union的成員作為定義的作用域的一部分直接訪問。Because an anonymous union provides no way to access its members, the members are directly accessible as part of the scope where the anonymous union is defined.
class Token {
public:
// indicates which kind of token value is in val
enum TokenKind {INT, CHAR, DBL};
TokenKind tok;
union { // anonymous union
char cval;
int ival;
double dval;
};
};
|
訪問:
Token token;
switch (token.tok) {
case Token::INT:
token.ival = 42; break;
case Token::CHAR:
token.cval = 'a'; break;
case Token::DBL:
token.dval = 3.14; break;
}
|
18.6 局部類Local Classes
跳過,跳過。。。
18.7固有的不可移植的特征( Inherently Nonportable Features)
portable:可移植的。
這一節(jié)所涉及的內(nèi)容:
· 如何更加容易的和硬件接口:位域和volatile。
· 如何更加容易的于其它語言的程序接口:鏈接指示linkage directives。
18.7.1. 位域
位域在內(nèi)存中的存儲和機器相關(guān)。The layout in memory of a bit-field is machine-dependent.
typedef unsigned int Bit;
class File {
Bit mode: 2;
Bit modified: 1;
Bit prot_owner: 3;
Bit prot_group: 3;
Bit prot_world: 3;
// ...
};
|
關(guān)于位域有很多特殊性,羅列出來:
- 位域最好是無符號類型。it is best to make a bit-field an unsigned type.
- 地址操作符(&)不能應用于位域,因此也就不存在有指向位域的指針。The address-of operator (&) cannot be applied to a bit-field
- 位域也不能是類的靜態(tài)成員。Nor can a bit-field be a static member of its class.
- 超過一個比特的位域通常要使用內(nèi)置的位操作符來控制。Bit-fields with more than one bit are usually manipulated using the built-in bitwise operators
enum { READ = 01, WRITE = 02 }; // File modes
int main() {
File myFile;
myFile.mode |= READ; // set the READ bit
if (myFile.mode & READ) // if the READ bit is on
cout << "myFile.mode READ is set"n";
}
|
18.7.2. volatile 限定符
當一個對象是通過編譯器控制或者檢測以外的方式修改,這個對象就聲明為volatile 。這意味著編譯器不會對這個對象進行優(yōu)化。An object should be declared volatile when its value might be changed in ways outside either the control or detection of the compiler.
volatile Task *curr_task; //執(zhí)行volatile Task對象的指針
volatile int ixa[max_size];
volatile Screen bitmap_buf; //數(shù)據(jù)成員都是volatile
|
羅列出噩夢般的N多種寫法的含義L
volatile int v; // v is a volatile int
int *volatile vip; // vip是指向int的volatile指針
volatile int *ivp; // ivp是指向volatile int的指針
volatile int *volatile vivp;// vivp是指向volatile int的volatile指針
int *ip = &v; // error: must use pointer to volatile
*ivp = &v; // ok: ivp is pointer to volatile
vivp = &v; // ok: vivp is volatile pointer to volatile
|
18.7.3. 鏈接指示: extern "C"
目的是為了解決程序調(diào)用其它語言的程序的函數(shù)。
C++使用連接指示linkage directives來指明非C++函數(shù)的語言。
聲明非C++函數(shù)
// 單個聲明
extern "C" size_t strlen(const char *);
// 復合聲明
extern "C" {
int strcmp(const char*, const char*);
char *strcat(char*, const char*);
}
|
導出C++函數(shù)到其它語言
extern "C" double calc(double dparm) { /* ... */ }
|
重載函數(shù)和鏈接指示
這取決于編程語言是不是支持重載,如果是C,不支持重載,那當然就不行嘍。
在一組重載函數(shù)中只能為一個 C 函數(shù)指定鏈接指示。a linkage directive can be specified only for one C function in a set of overloaded functions.
// error: two extern "C" functions in set of overloaded functions
extern "C" void print(const char*);
extern "C" void print(int);
|
函數(shù)指針
聲明:
注意:因為這是一個C指針,因此它是不能指向C++函數(shù)的。A pointer to a C function does not have the same type as a pointer to a C++ function.
//pf是一個指向C函數(shù)的指針,函數(shù)的返回值是void;形參是int。
extern "C" void (*pf)(int);
|