PS,1880后程序員

          看不完的牙,寫不完的程序,跑不完的步。
          隨筆 - 97, 文章 - 34, 評論 - 10, 引用 - 0

          導(dǎo)航

          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          公告

          常用鏈接

          留言簿(1)

          隨筆分類(6)

          隨筆檔案(51)

          文章分類(34)

          文章檔案(33)

          搜索

          •  

          最新評論

          C++ Primer 之 讀書筆記 第十八章 特殊工具與技術(shù)

           

          Chapter 18. 特殊工具與技術(shù)

           

          18.1 優(yōu)化內(nèi)存分配Optimizing Memory Allocation

          分配內(nèi)存和創(chuàng)建對象是兩個(gè)不同的概念。分配內(nèi)存,并不一定會(huì)立即創(chuàng)建對象。同樣,內(nèi)存釋放和對象撤銷也是兩個(gè)不同的概念。

           

          18.1.1. C++ 中的內(nèi)存分配

          new操作:給指定的類型分配內(nèi)存并在新分配的內(nèi)存上構(gòu)造指定類型的對象。

          在未構(gòu)造的內(nèi)存上給對象賦值,而不是初始化這個(gè)對象,這種賦值是未定義的。對很多類來說,這樣做會(huì)導(dǎo)致運(yùn)行期崩潰。賦值包括刪除現(xiàn)有的對象,如果不存在現(xiàn)有對象,那么這個(gè)賦值操作就會(huì)導(dǎo)致災(zāi)難性的結(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)存

          1. allocator
          2. newdelete操作符

          C++提供兩種方法創(chuàng)建和銷毀對象:

          1. allocator
          2. new操作符
          3. 直接調(diào)用對象的析構(gòu)函數(shù)(但是此時(shí)只是把對象占用的內(nèi)存變成了為構(gòu)造內(nèi)存,但是這個(gè)內(nèi)存并沒有釋放哦)
          4. uninitialized_fill and uninitialized_copy,是拷貝不是賦值

          18.1.2. allocator

          這是一個(gè)模板類,提供類型化的內(nèi)存分配,對象創(chuàng)建和撤銷。它把內(nèi)存分配和對象創(chuàng)建這兩件事情分開來。當(dāng)一個(gè)allocator對象分配內(nèi)存時(shí)間,它只是根據(jù)給定的類型分配出空間,這個(gè)空間的大小可以用來保存給定類型的對象。但是這時(shí),所分配的空間還沒有構(gòu)建。這就相當(dāng)于預(yù)先分配preallocation

          allocator<T> a;

          定義allocator對象a,按類型T分配內(nèi)存。

          a.allocate(n)

          分配未構(gòu)造內(nèi)存,這個(gè)內(nèi)存的大小是n個(gè)類型T的對象

          使用allocator管理類成員數(shù)據(jù)

          這里是用vector類來舉例說明。從vector的工作機(jī)制中可以看到:如果沒有空閑的單元,vector重新分配內(nèi)存。這樣獲得新的空間后,vector拷貝當(dāng)前現(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.因此從這個(gè)工作機(jī)理中可以看出vector的效率是比較差的。

          下面的這段代碼很好的說明了alloctor是如何管理類成員數(shù)據(jù)的。

          1. allocator分配內(nèi)存空間一定是和類型相關(guān)的。因此在Vector里面定義了靜態(tài)成員alloc

          static std::allocator<T> alloc; // object to get raw memory

          1. alloc.allocate()返回值是一個(gè)指定類型的指針。
          2. 先調(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 newoperator delete和標(biāo)準(zhǔn)庫中的其它操作符有些不同,它們是不能重載的。

          operator newoperator 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 newoperator delete模擬allocator

          • 分配內(nèi)存空間

          T* newelements = alloc.allocate(newcapacity);

          改編成

          T* newelements = static_cast<T*>(operator new[](newcapacity * sizeof(T)));

          • 釋放內(nèi)存空間

          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 表達(dá)式Placement new Expressions

          定位new表達(dá)式實(shí)際上就是要實(shí)現(xiàn)在已經(jīng)分配的內(nèi)存空間上構(gòu)造對象的功能。

          基本形式:

          new (place_address) type

          new (place_address) type (initializer-list)

          對比construct

          alloc.construct(first_free, t);

          等價(jià)于:

          new (first_free) T(t);

          ·         定位new表達(dá)式更加靈活。通過下面的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.                當(dāng)定位new表達(dá)式初始化一個(gè)對象時(shí),它使用構(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)造臨時(shí)對象然后再拷貝它。

          ·         另外有些類是不支持copy構(gòu)造函數(shù)的,這種情況下就只能用定位new了。

          18.1.5. 顯式析構(gòu)函數(shù)的調(diào)用(Explicit Destructor Invocation)

          既然可以通過定位new調(diào)用構(gòu)造函數(shù),那么對應(yīng)于此,還可以通過顯示調(diào)用析構(gòu)函數(shù)來取消對象。

          調(diào)用操作符delete不會(huì)執(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 deleteClass Specific new and delete

          這部分要說明的是類如何優(yōu)化new表達(dá)式的行為(optimizing the behavior of new expressions.)。

          這件事的理論依據(jù)是,類通過定義自己的成員,操作符newdelete,就可以管理其對象使用的內(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.

          當(dāng)編譯器看到類類型的newdelete表達(dá)式的時(shí)候,它先看這個(gè)類是不是定義了操作符newdelete成員。如果類定義自己的這兩個(gè)操作符函數(shù),編譯器就直接調(diào)用這些函數(shù)分配和釋放對象的內(nèi)存。否則,編譯器調(diào)用標(biāo)準(zhǔn)庫里的newdelete版本。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.

          newdelete成員函數(shù)

          重載newdelete包括一些限定條件:

          1. 首先必須是成對出現(xiàn),就是說如果重載new,也必須重載deleteIf a class defines either of these members, it should define both of them.
          2. 必須定義為靜態(tài)函數(shù)static

          L需要復(fù)習(xí)下下virtual析構(gòu)函數(shù),不virtual那又怎么樣?

          當(dāng)一個(gè)基類指針指向的是一個(gè)派生類的對象時(shí),如果析構(gòu)函數(shù)是virtual的,那么編譯器在運(yùn)行期動(dòng)態(tài)綁定到底調(diào)用哪個(gè)類的析構(gòu)函數(shù)。但是如果析構(gòu)函數(shù)沒有聲明為virtual,編譯器在編譯期就確定調(diào)用基類的析構(gòu)函數(shù),這就是靜態(tài)綁定

          new

          返回值類型是void*,形參類型是size_t

          delete

          返回值類型是void,形參類型是一個(gè)參數(shù)的void*,或者是兩個(gè)參數(shù)的void*size_t

          不得不說這部分講的太簡單了,只好從Google上再找些資料學(xué)習(xí)哈。

          定義:

          #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

          如何強(qiáng)制調(diào)用全局的操作符newdelete

          Type *p = ::new Type; // uses global operator new

          ::delete p;           // uses global operator delete

          18.1.7. 一個(gè)內(nèi)存分配器基類(A Memory-Allocator Base Class)

          這個(gè)部分就是這一節(jié)所講內(nèi)容的一個(gè)完整的實(shí)例。

          CachedObj可以看做是一種freelist

          freelist是啥?Google了一下下,KAO,原來有N多種freelist的實(shí)現(xiàn)。這是一種內(nèi)存管理技術(shù),包括預(yù)先分配沒有構(gòu)造對象的內(nèi)存,對象只在需要時(shí)在這些內(nèi)存上創(chuàng)建。當(dāng)對象釋放時(shí),它們所占用的內(nèi)存返回給預(yù)先分配的內(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)成員負(fù)責(zé)管理freelist,對于每種類型只需要一個(gè)freelist

                static std::allocator<T> alloc_mem;

                //freestor就是指向freelist表頭的指針。

                static T *freeStore;

                //chunk是一次預(yù)分配內(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表達(dá)式:QueueItem<Type> *pt = new QueueItem<Type>(val);

          1. 使用QueueItem<T>::operator newfreelist為對象分配空間。
          2. 在分配的空間上,使用類型Tcopy構(gòu)造函數(shù)構(gòu)建對象。

          deletedelete pt;

          1. 執(zhí)行QueueItem的析構(gòu)函數(shù)。
          2. 把對象所占用的內(nèi)存返還給freelist

          定義操作符new

          功能:從freelist里獲得一個(gè)對象的內(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)存單元追加到freelistfreelist是個(gè)鏈表,所以把數(shù)組轉(zhuǎn)換成鏈表,這樣每個(gè)對象的next指針都指向下一個(gè)對象

                     for (size_t i = 0; i != chunk; ++i)

                           add_to_freelist(&array[i]);

                 }

                 // freestore總是指向下一個(gè)有效的單元

                 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)

           {

              //這是一個(gè)小技巧,為的是避免調(diào)用派生類的next成員(如果存在)

              p->CachedObj<T>::next = freeStore;

              freeStore = p;

           }

          18.2 運(yùn)行期類型識別Run-Time Type Identification

          通過兩個(gè)操作符提供RTTI

          1. typeid:返回指針或者引用指向的對象的實(shí)際類型。
          2. dynamic_cast:把指向基類的對象的指針或者引用轉(zhuǎn)換成派生類的指針或引用。

          對于具有虛函數(shù)的類,RTTI在運(yùn)行期執(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:動(dòng)態(tài)強(qiáng)制類型轉(zhuǎn)換

          使用動(dòng)態(tài)強(qiáng)制類型轉(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操作符實(shí)際上執(zhí)行了兩種操作:

          1. 驗(yàn)證要執(zhí)行的強(qiáng)制類型轉(zhuǎn)換是不是有效。It begins by verifying that the requested cast is valid.
          2. 只有當(dāng)強(qiáng)制類型轉(zhuǎn)換有效時(shí),操作符才執(zhí)行實(shí)際的轉(zhuǎn)換操作。Only if the cast is valid does the operator actually do the cast.

          使用dynamic_cast 操作符

          大師給出了dynamic_cast操作符的使用方法,并例舉出這樣做的三大好處,總而言之就是盡量把代碼出錯(cuò)的幾率降到最小。

          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 操作符保證強(qiáng)制轉(zhuǎn)換以及轉(zhuǎn)換結(jié)果測試在一個(gè)表達(dá)式中。Performing a dynamic_cast in a condition ensures that the cast and test of its result are done in a single expression.

          這看上去很簡單,但是很重要,因?yàn)檫@樣可以降低程序出錯(cuò)的概率。

          使用 dynamic_cast 和引用類型

          對于引用類型的強(qiáng)制轉(zhuǎn)換,和指針的略有不同,這是因?yàn)橐貌荒苁强?/span>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 操作符

          當(dāng)typeid操作符的操作數(shù)不是類類型或者是類類型但是不包含虛函數(shù),typeid操作符指定的是操作數(shù)的靜態(tài)類型。當(dāng)操作數(shù)是定義了至少一個(gè)虛函數(shù)的類類型時(shí),類型是在運(yùn)行期計(jì)算出來的。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 的使用

          這是一個(gè)RTTI使用的簡單的例子:

          問題提出:如何定義基類、派生類的相等操作符

          相等的含義是:

          (1) 類型相同

          (2) 指定數(shù)據(jù)成員的值相同

          愚蠢的方法是針對每個(gè)派生類的組合都去重載操作符“==”。至于多愚蠢想象一下就夠了哦J

          18.2.4. type_info

          • 這個(gè)類和編譯器相關(guān),不同的編譯器對這個(gè)類的定義可能存在差異
          • type_info類的構(gòu)造函數(shù)和拷貝各種哦函數(shù)都是私有的private,唯一獲得type_info對象的方法就是通過typeid操作符。
          • 成員函數(shù)name的返回值依賴于編譯器。但是對于同一個(gè)類型,它的返回值是唯一的。這句話的含義是代碼中是不能出現(xiàn)if(typeid(obj).name()==”string”)這樣的代碼,因?yàn)椴煌木幾g器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是一個(gè)指向Screen類的無形參的get函數(shù)的指針。

          Screenget函數(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í)還是指針,是指針,就對應(yīng)有解引用操作(*)和箭頭操作(->)

          使用指向成員函數(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

          應(yīng)用

          成員指針的一個(gè)具體的應(yīng)用就是成員指針函數(shù)列表(Pointer-to-Member Function Tables)。說來也很簡單,就是把函數(shù)指針保存成Array,根據(jù)下標(biāo)來索引調(diào)用哪一個(gè)函數(shù)。因?yàn)檫@些函數(shù)被定義成了類成員函數(shù),這就用到了成員函數(shù)指針。這也算是一種典型的應(yīng)用了。

          18.4 內(nèi)嵌類Nested Classes

          這內(nèi)嵌類實(shí)際上是在它的外圍類enclosing class里定義了一種新的類型成員。A nested class defines a type member in its enclosing class.

          哦。。。這讓我想到的是Java中的內(nèi)部類,這兩個(gè)東西是不是類似L

          18.4.1 實(shí)現(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)嵌類成員

          在哪里定義?

          1. 必須和外圍類的定義在同一個(gè)作用域里。
          2. 如果內(nèi)嵌類的成員在自身類的外面定義,那么她是不能定義在外圍類里的。很好理解哦,內(nèi)嵌類的成員不是外圍類的成員嘛。

          下面這個(gè)例子(噢噢,不是花生),就是定義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

           };

          在另一個(gè)文件中定義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)嵌模板實(shí)例化

          當(dāng)外圍類模板實(shí)例化時(shí),內(nèi)嵌類是不會(huì)自動(dòng)實(shí)例化的。A nested class of a class template is not instantiated automatically when the enclosing class template is instantiated.內(nèi)嵌類只有在上下文中用到時(shí)才實(shí)例化。就是說:Queue<int> qi;只是實(shí)例化了Queue<int>而沒有實(shí)例化 QueueItem<int>

          18.4.2 內(nèi)嵌類作用域的名字查找

          當(dāng)處理類成員聲明時(shí),。當(dāng)處理定義時(shí),完整的內(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;

           };

          限制條件:

          1. 不能包含有靜態(tài)成員。
          2. 不能包含引用。
          3. 不能包含那些具有構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值操作符的類類型的對象。
          4. 不能包含有虛函數(shù)。

          使用

          定義

          // 初始化TokenValue,但是只能為第一個(gè)成員使用初始化式

          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

          因?yàn)槟涿?/span>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)存中的存儲(chǔ)和機(jī)器相關(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)于位域有很多特殊性,羅列出來:

          1. 位域最好是無符號類型。it is best to make a bit-field an unsigned type.
          2. 地址操作符(&)不能應(yīng)用于位域,因此也就不存在有指向位域的指針。The address-of operator (&) cannot be applied to a bit-field
          3. 位域也不能是類的靜態(tài)成員。Nor can a bit-field be a static member of its class.
          4. 超過一個(gè)比特的位域通常要使用內(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 限定符

          當(dāng)一個(gè)對象是通過編譯器控制或者檢測以外的方式修改,這個(gè)對象就聲明為volatile 。這意味著編譯器不會(huì)對這個(gè)對象進(jìn)行優(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是指向intvolatile指針

          volatile int *ivp; // ivp是指向volatile int的指針

          volatile int *volatile vivp;// vivp是指向volatile intvolatile指針

          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ù)

          // 單個(gè)聲明

          extern "C" size_t strlen(const char *);

          // 復(fù)合聲明

          extern "C" {

              int strcmp(const char*, const char*);

              char *strcat(char*, const char*);

          }

          導(dǎo)出C++函數(shù)到其它語言

          extern "C" double calc(double dparm) { /* ... */ }

          重載函數(shù)和鏈接指示

          這取決于編程語言是不是支持重載,如果是C,不支持重載,那當(dāng)然就不行嘍。

          在一組重載函數(shù)中只能為一個(gè) 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ù)指針

          聲明:

          注意:因?yàn)檫@是一個(gè)C指針,因此它是不能指向C++函數(shù)的。A pointer to a C function does not have the same type as a pointer to a C++ function.

          //pf是一個(gè)指向C函數(shù)的指針,函數(shù)的返回值是void;形參是int

          extern "C" void (*pf)(int);

          posted on 2009-08-28 08:35 amenglai 閱讀(773) 評論(0)  編輯  收藏 所屬分類: C++ Primer 之 讀書筆記

          主站蜘蛛池模板: 镇远县| 荃湾区| 竹北市| 龙门县| 黄浦区| 嘉峪关市| 淮北市| 砚山县| 体育| 南开区| 蒙自县| 泽州县| 福鼎市| 筠连县| 冷水江市| 资中县| 梁河县| 萨迦县| 布拖县| 汝阳县| 廊坊市| 前郭尔| 蒙自县| 祁门县| 新绛县| 石门县| 唐海县| 老河口市| 古田县| 琼结县| 常州市| 东莞市| 高尔夫| 阳信县| 镇巴县| 读书| 宝山区| 丰顺县| 汉阴县| 卓尼县| 托克逊县|