<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    PS,1880后程序員

    看不完的牙,寫(xiě)不完的程序,跑不完的步。
    隨筆 - 97, 文章 - 34, 評(píng)論 - 10, 引用 - 0
    數(shù)據(jù)加載中……

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

     

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

     

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

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

     

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

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

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

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

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

    18.1.2. allocator 類(lèi)

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

    allocator<T> a;

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

    a.allocate(n)

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

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

    這里是用vector類(lèi)來(lái)舉例說(shuō)明。從vector的工作機(jī)制中可以看到:如果沒(méi)有空閑的單元,vector重新分配內(nèi)存。這樣獲得新的空間后,vector拷貝當(dāng)前現(xiàn)有的對(duì)象到新的內(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的效率是比較差的。

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

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

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

    1. alloc.allocate()返回值是一個(gè)指定類(lèi)型的指針。
    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)庫(kù)中的其它操作符有些不同,它們是不能重載的。

    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ù)更為類(lèi)型安全。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)造對(duì)象的功能。

    基本形式:

    new (place_address) type

    new (place_address) type (initializer-list)

    對(duì)比construct

    alloc.construct(first_free, t);

    等價(jià)于:

    new (first_free) T(t);

    ·         定位new表達(dá)式更加靈活。通過(guò)下面的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è)對(duì)象時(shí),它使用構(gòu)造函數(shù),直接創(chuàng)建對(duì)象。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í)對(duì)象然后再拷貝它。

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

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

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

    調(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. 類(lèi)特定的 new deleteClass Specific new and delete

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

    這件事的理論依據(jù)是,類(lèi)通過(guò)定義自己的成員,操作符newdelete,就可以管理其對(duì)象使用的內(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)編譯器看到類(lèi)類(lèi)型的newdelete表達(dá)式的時(shí)候,它先看這個(gè)類(lèi)是不是定義了操作符newdelete成員。如果類(lèi)定義自己的這兩個(gè)操作符函數(shù),編譯器就直接調(diào)用這些函數(shù)分配和釋放對(duì)象的內(nèi)存。否則,編譯器調(diào)用標(biāo)準(zhǔn)庫(kù)里的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. 首先必須是成對(duì)出現(xiàn),就是說(shuō)如果重載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è)基類(lèi)指針指向的是一個(gè)派生類(lèi)的對(duì)象時(shí),如果析構(gòu)函數(shù)是virtual的,那么編譯器在運(yùn)行期動(dòng)態(tài)綁定到底調(diào)用哪個(gè)類(lèi)的析構(gòu)函數(shù)。但是如果析構(gòu)函數(shù)沒(méi)有聲明為virtual,編譯器在編譯期就確定調(diào)用基類(lèi)的析構(gòu)函數(shù),這就是靜態(tài)綁定

    new

    返回值類(lèi)型是void*,形參類(lèi)型是size_t

    delete

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

    不得不說(shuō)這部分講的太簡(jiǎn)單了,只好從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[]

    覆蓋類(lèi)特定的內(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)存分配器基類(lèi)(A Memory-Allocator Base Class)

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

    CachedObj可以看做是一種freelist

    freelist是啥?Google了一下下,KAO,原來(lái)有N多種freelist的實(shí)現(xiàn)。這是一種內(nèi)存管理技術(shù),包括預(yù)先分配沒(méi)有構(gòu)造對(duì)象的內(nèi)存,對(duì)象只在需要時(shí)在這些內(nèi)存上創(chuàng)建。當(dāng)對(duì)象釋放時(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中添加對(duì)象

          static void add_to_freelist(T*);

          //靜態(tài)成員負(fù)責(zé)管理freelist,對(duì)于每種類(lèi)型只需要一個(gè)freelist

          static std::allocator<T> alloc_mem;

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

          static T *freeStore;

          //chunk是一次預(yù)分配內(nèi)存空間的大小。

          static const std::size_t chunk;

     };

    使用CachedObj - 派生類(lèi)定義

    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為對(duì)象分配空間。
    2. 在分配的空間上,使用類(lèi)型Tcopy構(gòu)造函數(shù)構(gòu)建對(duì)象。

    deletedelete pt;

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

    定義操作符new

    功能:從freelist里獲得一個(gè)對(duì)象的內(nèi)存。

    template <class T>

     void *CachedObj<T>::operator new(size_t sz)

     {

           // 鏈表中的數(shù)據(jù)類(lèi)型必須一致

           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è)對(duì)象的next指針都指向下一個(gè)對(duì)象

               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操作符

    功能:就是要把對(duì)象占用的內(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)用派生類(lèi)的next成員(如果存在)

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

        freeStore = p;

     }

    18.2 運(yùn)行期類(lèi)型識(shí)別Run-Time Type Identification

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

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

    對(duì)于具有虛函數(shù)的類(lèi),RTTI在運(yùn)行期執(zhí)行;對(duì)于其它的類(lèi)型是在編譯期執(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)制類(lèi)型轉(zhuǎn)換

    使用動(dòng)態(tài)強(qiáng)制類(lèi)型轉(zhuǎn)換要小心。在任何可能的情況下,定義和使用虛函數(shù)比直接接管類(lèi)型管理好得多。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)制類(lèi)型轉(zhuǎn)換是不是有效。It begins by verifying that the requested cast is valid.
    2. 只有當(dāng)強(qiáng)制類(lèi)型轉(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

     }

    在條件語(yǔ)句中使用dynamic_cast 操作符保證強(qiáng)制轉(zhuǎn)換以及轉(zhuǎn)換結(jié)果測(cè)試在一個(gè)表達(dá)式中。Performing a dynamic_cast in a condition ensures that the cast and test of its result are done in a single expression.

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

    使用 dynamic_cast 和引用類(lèi)型

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

    問(wèn)題提出:如何定義基類(lèi)、派生類(lèi)的相等操作符

    相等的含義是:

    (1) 類(lèi)型相同

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

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

    18.2.4. type_info 類(lèi)

    • 這個(gè)類(lèi)和編譯器相關(guān),不同的編譯器對(duì)這個(gè)類(lèi)的定義可能存在差異
    • type_info類(lèi)的構(gòu)造函數(shù)和拷貝各種哦函數(shù)都是私有的private,唯一獲得type_info對(duì)象的方法就是通過(guò)typeid操作符。
    • 成員函數(shù)name的返回值依賴(lài)于編譯器。但是對(duì)于同一個(gè)類(lèi)型,它的返回值是唯一的。這句話的含義是代碼中是不能出現(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 類(lèi)成員指針Pointer to Class Member

    定義

    指向數(shù)據(jù)成員的指針

    string Screen::*ps_Screen = &Screen::contents;

    指向成員函數(shù)的指針

    char (Screen::*pmf)() const = &Screen::get;

    pmf是一個(gè)指向Screen類(lèi)的無(wú)形參的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;

    這樣上面的定義就可以簡(jiǎn)化成:

    Action get = &Screen::get;

    使用類(lèi)成員指針

    類(lèi)成員指針說(shuō)來(lái)說(shuō)去的,其實(shí)還是指針,是指針,就對(duì)應(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)。說(shuō)來(lái)也很簡(jiǎn)單,就是把函數(shù)指針保存成Array,根據(jù)下標(biāo)來(lái)索引調(diào)用哪一個(gè)函數(shù)。因?yàn)檫@些函數(shù)被定義成了類(lèi)成員函數(shù),這就用到了成員函數(shù)指針。這也算是一種典型的應(yīng)用了。

    18.4 內(nèi)嵌類(lèi)Nested Classes

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

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

    18.4.1 實(shí)現(xiàn)內(nèi)嵌類(lèi)

    定義內(nèi)嵌類(lè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和它的友元可以訪問(wèn)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)嵌類(lèi)成員

    在哪里定義?

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

    在外圍類(lèi)之外定義內(nèi)嵌類(lèi)

    定義Queue類(lèi),只要前向聲明QueueItem是內(nèi)嵌類(lè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)外圍類(lèi)模板實(shí)例化時(shí),內(nèi)嵌類(lè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)嵌類(lèi)只有在上下文中用到時(shí)才實(shí)例化。就是說(shuō):Queue<int> qi;只是實(shí)例化了Queue<int>而沒(méi)有實(shí)例化 QueueItem<int>

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

    當(dāng)處理類(lèi)成員聲明時(shí),。當(dāng)處理定義時(shí),完整的內(nèi)嵌類(lèi)或者外圍類(lè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ù)和賦值操作符的類(lèi)類(lèi)型的對(duì)象。
    4. 不能包含有虛函數(shù)。

    使用

    定義

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

    TokenValue first_token = {'a'}; 

    // 未初始化TokenValue對(duì)象

    TokenValue last_token;         

    // 定義指向TokenValue對(duì)象的指針

    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沒(méi)有提供訪問(wèn)其成員的方法,因此Union的成員作為定義的作用域的一部分直接訪問(wèn)。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;

         };

     };

    訪問(wèn):

    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 局部類(lèi)Local Classes

    跳過(guò),跳過(guò)。。。

    18.7固有的不可移植的特征( Inherently Nonportable Features

    portable:可移植的。

    這一節(jié)所涉及的內(nèi)容:

    ·         如何更加容易的和硬件接口:位域和volatile

    ·         如何更加容易的于其它語(yǔ)言的程序接口:鏈接指示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)于位域有很多特殊性,羅列出來(lái):

    1. 位域最好是無(wú)符號(hào)類(lèi)型。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. 位域也不能是類(lèi)的靜態(tài)成員。Nor can a bit-field be a static member of its class.
    4. 超過(guò)一個(gè)比特的位域通常要使用內(nèi)置的位操作符來(lá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è)對(duì)象是通過(guò)編譯器控制或者檢測(cè)以外的方式修改,這個(gè)對(duì)象就聲明為volatile 。這意味著編譯器不會(huì)對(duì)這個(gè)對(duì)象進(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對(duì)象的指針

    volatile int ixa[max_size]; 

    volatile Screen bitmap_buf; //數(shù)據(jù)成員都是volatile

    羅列出噩夢(mèng)般的N多種寫(xiě)法的含義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)用其它語(yǔ)言的程序的函數(shù)。

    C++使用連接指示linkage directives來(lái)指明非C++函數(shù)的語(yǔ)言。

    聲明非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ù)到其它語(yǔ)言

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

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

    這取決于編程語(yǔ)言是不是支持重載,如果是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 閱讀(770) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): C++ Primer 之 讀書(shū)筆記

    主站蜘蛛池模板: 亚洲一本之道高清乱码| 国产成人免费片在线视频观看| 成年性羞羞视频免费观看无限| 国产在线观看麻豆91精品免费| 久久国产精品成人片免费| 性一交一乱一视频免费看| 日本免费人成黄页在线观看视频 | 亚洲综合免费视频| 国产在线观看片a免费观看| 成熟女人特级毛片www免费| 精品亚洲视频在线观看| 亚洲伊人久久大香线蕉在观| 亚洲videosbestsex日本| 一级女性全黄生活片免费看| 国产成人免费午夜在线观看| 成年女人毛片免费播放视频m| 久久亚洲欧洲国产综合| 亚洲AV无码专区在线亚| 中文字幕在线视频免费| 天天操夜夜操免费视频| 亚洲国产成人久久综合一 | 亚洲综合久久成人69| 污视频网站免费观看| 国产精品内射视频免费| 久久99九九国产免费看小说| 亚洲色无码专区在线观看| 亚洲中文精品久久久久久不卡| 国产成人精品亚洲| h视频在线免费看| 亚洲精品无码不卡在线播HE| 亚洲jizzjizz少妇| 国产精品美女久久久免费| 午夜一级免费视频| 91在线精品亚洲一区二区| 一道本不卡免费视频| 永久免费av无码网站大全| 亚洲精品日韩专区silk| 国产一级a毛一级a看免费人娇| 拔擦拔擦8x华人免费久久| 亚洲人6666成人观看| 99视频精品全部免费观看|