• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            1.為啥要重載操作符:

            通過(guò)重載操作符,程序員可以針對(duì)“類”類型的操作數(shù)定義不同的操作符版本。良好的操作符定義可以使class類型的使用想內(nèi)置類型一樣直觀簡(jiǎn)潔,使用重定義的操作符而不是命名函數(shù)使得程序可以用表達(dá)式代替函數(shù)調(diào)用,使程序編寫和閱讀更容易~

            2.哪些不能重載

            ::     .*      .     ?:    這些不能重載

            3.需要注意的地方:

            重載必須有一個(gè)class類型的操作數(shù),短路求值失去,

            重載操作符和內(nèi)置操作符結(jié)合型相同,優(yōu)先級(jí)操作數(shù)個(gè)數(shù)均相同

            不要重載一些含有內(nèi)置定義的操作符 & , && || 這些

            ·賦值(=)下標(biāo)(【】)調(diào)用 (())和成員訪問(wèn)操作符必須定義為成員

            ·對(duì)稱的操作符一般定義為普通非成員函數(shù)

            ++ -- 一般設(shè)置為成員函數(shù) ,因?yàn)榭赡芨淖儗?duì)象狀態(tài)

            4.定義輸入輸出操作符

            io操作符只能重載為非成員函數(shù),否則做操作符只能是對(duì)象成員 用法變成了 object<<cin  不符合我們的習(xí)慣,經(jīng)常把他們?cè)O(shè)置成為友元,因?yàn)榭赡苡|及私有變量。

            輸入必須加入文件結(jié)束和輸入錯(cuò)誤的錯(cuò)誤處理

            istream&
            operator>>(istream& in, Sales_item& s)
            {
                 double price;
                 in >> s.isbn >> s.units_sold >> price;
                 // check that the inputs succeeded
                 if (in)
                    s.revenue = s.units_sold * price;
                 else
                    s = Sales_item(); // input failed: reset object to default state
                 return in;
            }

            如果輸入失敗,調(diào)用默認(rèn)構(gòu)造函數(shù)恢復(fù)對(duì)象狀態(tài)。

            5.算術(shù)運(yùn)算符和關(guān)系運(yùn)算符

            Sales_item& operator -=(const Sales_item& item){
                units_sold-=item.units_sold;
                revenue-=item.revenue;
                return *this;
            }
            Sales_item operator - (const Sales_item& lhs,const Sales_item& rhs){
                Sales_item res(lhs);
                res+=rhs;
                return res;
            }

             

            一般算術(shù)運(yùn)算符設(shè)置為非成員函數(shù),與內(nèi)置運(yùn)算符對(duì)應(yīng),選擇返回value 而不是引用。賦值運(yùn)算符重載為成員函數(shù),并用它來(lái)實(shí)現(xiàn)算術(shù)運(yùn)算符,這樣算術(shù)運(yùn)算符不用friend

            相等運(yùn)算符和不等運(yùn)算符一般成對(duì)出現(xiàn),且用一個(gè)實(shí)現(xiàn)另一個(gè)

            關(guān)系運(yùn)算符 == != > < 一般重載成非成員函數(shù)

            6.賦值操作符

            必須為成員函數(shù) (=號(hào))

            =和+=  -= 一般都需要返回左操作數(shù)的引用

            Sales_item& operator = (string is){
                isbn = is;
                return *this;
            }

            6.下標(biāo)操作符

            必須為類成員函數(shù)   返回引用使其可以在賦值操作的任意一邊

            一般定義一種const 返回常量引用 一種not const 引用

                 class Foo {
                 public:
                     int &operator[] (const size_t);
                     const int &operator[] (const size_t) const;
                     // other interface members
                 private:
                     vector<int> data;
                     // other member data and private utility functions
                  };

            int& Foo::operator[] (const size_t index)
                 {
                     return data[index];  // no range checking on index
                 }
                 const int& Foo::operator[] (const size_t index) const
                 {
                     return data[index];  // no range checking on index
                 }

            pari<string,string>& operator[] (const  vector< pair<string,string>* > ::size_type index){
                return *wait_list.at(index);//使用at判斷是否越界
            }

            6.成員訪問(wèn)操作符

            -> 一般要求重載為成員運(yùn)算符,*沒有要求 ,但成員比較常見~~~

            例子:auto-ptr~~~~

            ScreenPtr 的用戶將會(huì)傳遞一個(gè)指針,該指針指向動(dòng)態(tài)分配的 ScreenScreenPtr 類將擁有該指針,并安排在指向基礎(chǔ)對(duì)象的最后一個(gè) ScreenPtr 消失時(shí)刪除基礎(chǔ)對(duì)象。另外,不用為 ScreenPtr 類定義默認(rèn)構(gòu)造函數(shù)。因此,我們知道一個(gè) ScreenPtr 對(duì)象將總是指向一個(gè) Screen 對(duì)象,不會(huì)有未綁定的 ScreenPtr,這一點(diǎn)與內(nèi)置指針不同。應(yīng)用程序可以使用 ScreenPtr 對(duì)象而無(wú)須首先測(cè)試它是否指向一個(gè) Screen 對(duì)象。

                 // private class for use by ScreenPtr only 私有類,
                 class ScrPtr {
                     friend class ScreenPtr;
                     Screen *sp;
                     size_t use;
                     ScrPtr(Screen *p): sp(p), use(1) { }
                     ~ScrPtr() { delete sp; }
                 };
                    /*
                  * smart pointer: Users pass to a pointer to a dynamically allocated Screen, which
                  *                   is automatically destroyed when the last ScreenPtr goes away
                  */
                 class ScreenPtr {
                 public:
                     //  no default constructor: ScreenPtrs must be bound to an object
                     ScreenPtr(Screen *p): ptr(new ScrPtr(p)) { }
                     //  copy members and increment the use count
                     ScreenPtr(const ScreenPtr &orig):
                        ptr(orig.ptr) { ++ptr->use; }
                     ScreenPtr& operator=(const ScreenPtr&);
                     //  if use count goes to zero, delete the ScrPtr object
                     ~ScreenPtr() { if (--ptr->use == 0) delete ptr; }
                 private:
                     ScrPtr *ptr;    // points to use-counted ScrPtr class
                 };

            指針支持的基本操作有解引用操作和箭頭操作。我們的類可以這樣定義這些操作:

                 class ScreenPtr {
                 public:
                     // constructor and copy control members as before
                     Screen &operator*() { return *ptr->sp; }
                     Screen *operator->() { return ptr->sp; }
                     const Screen &operator*() const { return *ptr->sp; }
                     const Screen *operator->() const { return ptr->sp; }
                 private:
                     ScrPtr *ptr; // points to use-counted ScrPtr class
                 };

            解引用操作符是個(gè)一元操作符。在這個(gè)類中,解引用操作符定義為成員,因此沒有顯式形參,該操作符返回對(duì) ScreenPtr 所指向的 Screen 的引用。

            箭頭操作符不接受顯式形參。point->action();   等價(jià)于  (point->action)();

            可以這樣使用 ScreenPtr 對(duì)象訪問(wèn) Screen 對(duì)象的成員:

            ScreenPtr p(&myScreen);     // copies the underlying Screen
            p->display(cout);

            因?yàn)?p 是一個(gè) ScreenPtr 對(duì)象,p->display 的含義與對(duì) (p.operator->())->display 求值相同。對(duì) p.operator->() 求值將調(diào)用 ScreenPtr 類的 operator->,它返回指向 Screen 對(duì)象的指針,該指針用于獲取并運(yùn)行 ScreenPtr 所指對(duì)象的 display 成員。

            重載箭頭操作符必須返回指向類類型的指針,或者返回定義了自己的箭頭操作符的類類型對(duì)象。

            6.自增自減操作符

            一般重載為成員函數(shù),為了與內(nèi)置類型一致,前置操作符返回運(yùn)算結(jié)果引用,后置操作符返回運(yùn)算前的值,value not ref ,為了區(qū)分,后置操作符提供了一個(gè)實(shí)參0;

            // prefix: return reference to incremented/decremented object
                CheckedPtr& CheckedPtr::operator++()
                {
                    if (curr == end)
                        throw out_of_range
                              ("increment past the end of CheckedPtr");
                    ++curr;                // advance current state
                    return *this;
                }

            CheckedPtr CheckedPtr::operator++(int)
                 {

                     // no check needed here, the call to prefix increment will do the check
                     CheckedPtr ret(*this);        // save current value
                     ++*this;                      // advance one element, checking the increment 用前置實(shí)現(xiàn)它,不用判斷出界了
                     return ret;                   // return saved state
                 }

            顯式調(diào)用:

            CheckedPtr parr(ia, ia + size);        // iapoints to an array of ints
            parr.operator++(0);                    // call postfix operator++
            parr.operator++();                     // call prefix operator++

            7 調(diào)用操作符和函數(shù)對(duì)象

            struct absInt {
                   int operator() (int val) {
                       return val < 0 ? -val : val;
                   }
               };

             

            通過(guò)為類類型的對(duì)象提供一個(gè)實(shí)參表而使用調(diào)用操作符,所用的方式看起來(lái)像一個(gè)函數(shù)調(diào)用:

                 int i = -42;
                 absInt absObj;  // object that defines function call operator
                 unsigned int ui = absObj(i);     // calls absInt::operator(int)

            函數(shù)調(diào)用操作符必須聲明為成員函數(shù)。一個(gè)類可以定義函數(shù)調(diào)用操作符的多個(gè)版本,由形參的數(shù)目或類型加以區(qū)別。

            定義了調(diào)用操作符的類,其對(duì)象常稱為函數(shù)對(duì)象,即它們是行為類似函數(shù)的對(duì)象。

            函數(shù):

                 // determine whether a length of a given word is 6 or more
                 bool GT6(const string &s)
                 {
                     return s.size() >= 6;
                 }
            

            函數(shù)對(duì)象:

            // determine whether a length of a given word is longer than a stored bound
                 class GT_cls {
                 public:
                     GT_cls(size_t val = 0): bound(val) { }
                     bool operator()(const string &s)
                                        { return s.size() >= bound; }
                 private:
                     std::string::size_type bound;
                 };

            for (size_t i = 0; i != 11; ++i)
                 cout << count_if(words.begin(), words.end(), GT(i))
                      << " words " << i
                      << " characters or longer" << endl;

            函數(shù)對(duì)象的便捷性】

                     plus<int> intAdd;         // function object that can add two int values
                 negate<int> intNegate;   //  function object that can negate an int value
                 // uses intAdd::operator(int, int) to add 10 and 20
                 int sum = intAdd(10, 20);          // sum = 30
                 // uses intNegate::operator(int) to generate -10 as second parameter
                 // to intAdd::operator(int, int)
                 sum = intAdd(10, intNegate(10));    // sum = 0

            函數(shù)適配器:

            banding器,它通過(guò)將一個(gè)操作數(shù)綁定到給定值而將二元函數(shù)對(duì)象轉(zhuǎn)換為一元函數(shù)對(duì)象

            求反器是一種函數(shù)適配器,它將謂詞函數(shù)對(duì)象的真值求反。標(biāo)準(zhǔn)庫(kù)定義了兩個(gè)求反器:not1not2 分別求反一元二元對(duì)象

            8。實(shí)參匹配和轉(zhuǎn)換(俺來(lái)看重載操作符的原因啊,,,)

            轉(zhuǎn)換操作符是一種特殊的類成員函數(shù)。它定義將類類型值轉(zhuǎn)變?yōu)槠渌愋椭档霓D(zhuǎn)換。轉(zhuǎn)換操作符在類定義體內(nèi)聲明,在保留字 operator 之后跟著轉(zhuǎn)換的目標(biāo)類型:

            轉(zhuǎn)換函數(shù)采用如下通用形式:

                 operator type();
            

            轉(zhuǎn)換函數(shù)必須是成員函數(shù),不能指定返回類型,并且形參表必須為空。

            雖然轉(zhuǎn)換函數(shù)不能指定返回類型,但是每個(gè)轉(zhuǎn)換函數(shù)必須顯式返回一個(gè)指定類型的值。例如,operator int 返回一個(gè) int 值;如果定義 operator Sales_item,它將返回一個(gè) Sales_item 對(duì)象,諸如此類。

            轉(zhuǎn)換函數(shù)一般不應(yīng)該改變被轉(zhuǎn)換的對(duì)象。因此,轉(zhuǎn)換操作符通常應(yīng)定義為 const 成員。

            只要存在轉(zhuǎn)換,編譯器將在可以使用內(nèi)置轉(zhuǎn)換的地方自動(dòng)調(diào)用它

          1. In expressions:

            在表達(dá)式中:

                 SmallInt si;
                 double dval;
                 si >= dval          // si converted to int and then convert to double
          2. In conditions:

            在條件中:

                 if (si)                // si converted to int and then convert to bool
          3. When passing arguments to or returning values from a function:

            將實(shí)參傳給函數(shù)或從函數(shù)返回值:

                 int calc(int);
                 SmallInt si;
                 int i = calc(si);      // convert si to int and call calc
          4. As operands to overloaded operators:

            作為重載操作符的操作數(shù):

                 // convert si to int then call opeator<< on the int value
                 cout << si << endl;
          5. In an explicit cast:

            在顯式類型轉(zhuǎn)換中:

                 int ival;
                 SmallInt si = 3.541; //
                 instruct compiler to cast si to int
                 ival = static_cast<int>(si) + 3;
            

            類類型轉(zhuǎn)換之后不能再跟另一個(gè)類類型轉(zhuǎn)換。如果需要多個(gè)類類型轉(zhuǎn)換,則代碼將出錯(cuò)。(指的是不能連續(xù)兩個(gè)自定義的類型轉(zhuǎn)換,但是內(nèi)置類型轉(zhuǎn)換可以的)

          6. 還有一部分是實(shí)參匹配和轉(zhuǎn)換 ,沒時(shí)間了 以后再看~~~~

             

             

             

             

             

             

             

             

             

             

             

            posted @ 2010-03-15 16:40 rikisand 閱讀(1232) | 評(píng)論 (0)編輯 收藏

            在復(fù)習(xí)和學(xué)習(xí)新知識(shí)面前,人們總是傾向于學(xué)習(xí)新的知識(shí),以獲得更大的滿足感與成就感。寫博客其實(shí)更大程度是為了讓自己復(fù)習(xí),而不是將這種學(xué)習(xí)的成就感實(shí)體化。如果寫了記錄但從沒有回頭去看,去復(fù)習(xí),去溫故,當(dāng)下次遇到問(wèn)題時(shí)只能從頭學(xué)起。與第一次的區(qū)別僅僅在于,我知道我曾經(jīng)記過(guò)而已~

            所以,每周至少應(yīng)該抽出一天的時(shí)間,來(lái)回顧上周記下的東西。在這一天不要貪圖學(xué)習(xí)更多的新的知識(shí),如果通過(guò)復(fù)習(xí)把上周記下的知識(shí)鞏固,加深領(lǐng)會(huì),已經(jīng)是很大的收獲了···

            posted @ 2010-03-14 12:34 rikisand 閱讀(176) | 評(píng)論 (0)編輯 收藏

               首先,可重入和線程安全是兩個(gè)并不等同的概念,一個(gè)函數(shù)可以是可重入的,也可以是線程安全的,可以兩者均滿足,可以兩者皆不滿組(該描述嚴(yán)格的說(shuō)存在漏洞,參見第二條)。
                其次,從集合和邏輯的角度看,可重入是線程安全的子集,可重入是線程安全的充分非必要條件。可重入的函數(shù)一定是線程安全的,然過(guò)來(lái)則不成立。
                第三,POSIX 中對(duì)可重入和線程安全這兩個(gè)概念的定義:
            Reentrant Function:
                A function whose effect, when called by two or more threads,is guaranteed to be as if the threads each executed thefunction one after another in an undefined order, even ifthe actual execution is interleaved.
                                                                                                                    From IEEE Std 1003.1-2001 (POSIX 1003.1)
                                                                                                                                                  -- Base Definitions, Issue 6
            Thread-Safe Function:
                A function that may be safely invoked concurrently by multiple threads.
               另外還有一個(gè) Async-Signal-Safe的概念
            Async-Signal-Safe Function:
                A function that may be invoked, without restriction fromsignal-catching functions. No function is async-signal -safe unless explicitly described as such.
                以上三者的關(guān)系為:
            Reentrant Function 必然是Thread-Safe Function和Async-Signal-Safe Function
            可重入與線程安全的區(qū)別體現(xiàn)在能否在signal處理函數(shù)中被調(diào)用的問(wèn)題上,可重入函數(shù)在signal處理函數(shù)中可以被安全調(diào)用,因此同時(shí)也是Async-Signal-Safe Function;而線程安全函數(shù)不保證可以在signal處理函數(shù)中被安全調(diào)用,如果通過(guò)設(shè)置信號(hào)阻塞集合等方法保證一個(gè)非可重入函數(shù)不被信號(hào)中斷,那么它也是Async-Signal-Safe Function。
                 值得一提的是POSIX 1003.1的System Interface缺省是Thread-Safe的,但不是Async-Signal-Safe的。Async-Signal-Safe的需要明確表示,比如fork ()和signal()。
            最后讓我們來(lái)構(gòu)想一個(gè)線程安全但不可重入的函數(shù):
               假設(shè)函數(shù)func()在執(zhí)行過(guò)程中需要訪問(wèn)某個(gè)共享資源,因此為了實(shí)現(xiàn)線程安全,在使用該資源前加鎖,在不需要資源解鎖。
               假設(shè)該函數(shù)在某次執(zhí)行過(guò)程中,在已經(jīng)獲得資源鎖之后,有異步信號(hào)發(fā)生,程序的執(zhí)行流轉(zhuǎn)交給對(duì)應(yīng)的信號(hào)處理函數(shù);再假設(shè)在該信號(hào)處理函數(shù)中也需要調(diào)用函數(shù)func(),那么func()在這次執(zhí)行中仍會(huì)在訪問(wèn)共享資源前試圖獲得資源鎖,然而我們知道前一個(gè)func()實(shí)例已然獲得該鎖,因此信號(hào)處理函數(shù)阻塞——另一方面,信號(hào)處理函數(shù)結(jié)束前被信號(hào)中斷的線程是無(wú)法恢復(fù)執(zhí)行的,當(dāng)然也沒有釋放資源的機(jī)會(huì),這樣就出現(xiàn)了線程和信號(hào)處理函數(shù)之間的死鎖局面。
                因此,func()盡管通過(guò)加鎖的方式能保證線程安全,但是由于函數(shù)體對(duì)共享資源的訪問(wèn),因此是非可重入。

            posted @ 2010-03-12 14:33 rikisand 閱讀(253) | 評(píng)論 (0)編輯 收藏

            #include <iostream> 
            #include <vector>
            #include "time.h"
            using namespace std; 
            void sieve(int n){
                vector<bool> isprime(n,true);
                vector<int> prime;
                int cnt=0;
                for(int i=2;i<n;i++){
                    if(isprime[i])cnt++,prime.push_back(i);
                    for(int t=0;t<cnt&&i*prime[t]<n;t++){
                        isprime[i*prime[t]]=false;
                        if(i%prime[t]==0)break;
                    }
                }
                /*for(int i=0;i<cnt;i++)
                    cout<<prime[i]<<" ";*/
            }
            void oldseive(int n){
                vector<bool> isprime(n,true);
                vector<int> prime;
                for(int i=2;i<n;i++){
                    if(isprime[i]){
                        prime.push_back(i);
                        for(int j=i*2;j<n;j+=i)
                            isprime[j]=false;
                    }
                }
                /*for(int i=0;i<prime.size();i++)
                    cout<<prime[i]<<" ";*/
            }
            int main(){
                clock_t start,end;
                start = clock();
                 sieve(2000000);
                 //oldseive(2000000);
                end  = clock();
                double time = double(end-start)/CLOCKS_PER_SEC;
                cout<<endl<< time<<endl;
            } 

            線性篩法sieve 1.546s oldsieve 2.875s 快了將近一倍

            old sieve 缺陷:合數(shù)可能被多次篩掉,例如 30被2,3,5篩掉了3次 然后 線性篩法限定了 任何一個(gè)合數(shù)只被它的最小質(zhì)因數(shù)篩掉一次,怎么做到這一點(diǎn)~~

            if(i%prime[t]==0) break; 如果此時(shí)篩掉的合數(shù)從小到大找到第一個(gè)可以整除的質(zhì)數(shù),那么顯然他找到了它的最小質(zhì)因數(shù),此時(shí)我們停止搜索質(zhì)數(shù)表,因?yàn)楹竺娴馁|(zhì)數(shù)比當(dāng)前的prime[t]要大,如果我們用prime[t+n]*i 篩掉了一個(gè)合數(shù),這個(gè)合數(shù)必然可以表述成為 prime[t]*someK  *prime[t+n] 也就是說(shuō)這個(gè)合數(shù)的最小質(zhì)因數(shù)也是prime[t],他應(yīng)該被 prime[t]篩掉-->當(dāng)程序運(yùn)行到 someK*prime[t+n] 的時(shí)候~~~~

            over--------------------------------------------------------------------

            posted @ 2010-03-12 14:04 rikisand 閱讀(1547) | 評(píng)論 (0)編輯 收藏

            構(gòu)造,析構(gòu),拷貝語(yǔ)義學(xué) semantics of construction destruction and copy

            純虛擬函數(shù)的存在(Presence of a Pure Virtual Function)

            純虛擬函數(shù)是可以定義并調(diào)用的,只不過(guò)只能被靜態(tài)調(diào)用,不能經(jīng)由虛擬機(jī)制。(經(jīng)過(guò)試驗(yàn)vs2008是不可以的)

            而pure virtual destructor :class 設(shè)計(jì)者一定要定義它。因?yàn)槊恳粋€(gè)derived class的destructor會(huì)被編譯器加以擴(kuò)展,以靜態(tài)方式調(diào)用其每一個(gè)virtual base class 以及上層base class 的destructor ,因此缺乏任何一個(gè)base class destructor 的定義就會(huì)導(dǎo)致連接失敗

            基類的析構(gòu)函數(shù)應(yīng)該設(shè)置為虛擬的,不一定是純虛的 ,除非在希望將一個(gè)類變成抽象類(不能實(shí)例化的類),而這個(gè)類又沒有合適的函數(shù)可以被純虛化的時(shí)候,可以使用純虛的析構(gòu)函數(shù)來(lái)達(dá)到目的。

            class A{ public: char* pChar;A(){pChar=new char[20];};~A(){delete []pChar;}};

            class B:public A{ public: char* pChar;B(){pChar=new char[20];}~B(){delete []pChar;}};

            int main(){ A* a = new B; delete a;}

            如上,如果A的析構(gòu)函數(shù)沒有設(shè)置為虛擬的,那么delete a 的時(shí)候只有調(diào)用了A的析構(gòu)函數(shù),真正的B并沒有調(diào)用。而如果虛擬的話,那么a調(diào)用析構(gòu)函數(shù)會(huì)調(diào)用B的析構(gòu)函數(shù),而B的析構(gòu)函數(shù)會(huì)調(diào)用A的析構(gòu)函數(shù)。

            虛擬函數(shù)的取舍:如果一個(gè)函數(shù)被設(shè)計(jì)為虛擬函數(shù),但是他函數(shù)定義并不與類型相關(guān),也就是說(shuō)繼承類改寫他的幾率很低,那么這是個(gè)糟糕的事情。如果不是虛擬而且能內(nèi)聯(lián),這是個(gè)很大的損失

            ---------

            沒有繼承下的對(duì)象構(gòu)造

            Plain OI’s data 形式

            例如 struct {float x,y,z;}

            理論上編譯器會(huì)生成沒有用的trival ctor dtor copytor copy operator ,然而事實(shí)上什么也沒有發(fā)生。

            全局對(duì)象,c中會(huì)被放在bss段,而c++中的全局對(duì)象會(huì)被視為完全定義,被當(dāng)做初始化過(guò)的數(shù)據(jù)來(lái)對(duì)待。

            抽象數(shù)據(jù)類型:

            class Point {public: Point (float x=0):_x(x){}; private: _x;}

            經(jīng)過(guò)封裝的Point class 大小沒有改變 由于默認(rèn)的member wise 賦值語(yǔ)義已經(jīng)足夠,我們無(wú)需提供copy constructor 和copy operator。對(duì)于global 實(shí)體,Point::Point(0) 會(huì)自動(dòng)調(diào)用 (程序開始時(shí)候startup())

            如果要對(duì)class所有成員都設(shè)定常量初值,那么給予一個(gè)explicit initial list 會(huì)比較高效。

            例如 void mumble(){

            Point A1={1};

            Point A2; A2._x=1;

            }

            A2的初始化會(huì)比A1慢,因?yàn)椋?dāng)activation record(活動(dòng)記錄 是不是棧中的pc 指針),上述的list中常量就可以放進(jìn)A1的內(nèi)存中了

            explicit initialzation list (A1的那種)缺點(diǎn):public 才可以;只能指定常量;

            Point *heap = new Point;

            翻譯為:

            Point *heap = __new(sizeof(Point));

            if(heap)heap->Point::Point();

            `````

            為繼承準(zhǔn)備:

            class Point{
            public:
                Point(float x=0.,float y=0.):_x(x),_y(y){}
                virtual float z();
            protected:
                float _x,_y;
            };

            這里并沒有定義copy constructor ,copy operator,我們所有member 都是數(shù)值,因此默認(rèn)語(yǔ)義下情況良好。virtual destructor 也沒有引入啊,同樣道理,行為良好即可

            virtual func 的引入給我們的 Point class 帶來(lái)了膨脹作用:

            定義的構(gòu)造函數(shù)被附加了一些代碼,用來(lái)初始化vptr 這些代碼附加在任何base 初始化之后,任何程序員代碼之前。例如:

            Point* Point::Point(Point* this,float x,float y):_x(x),_y(y){

              this->_vptr_Point=_vtbl_Point; this->_x=x;this->_y=y; return this;

            }

            合成一個(gè)copy constructor 和一個(gè) copy operator 而且他們也不再是trival

            因?yàn)閎itwise的賦值可能會(huì)帶給vptr 錯(cuò)誤

            ···············

            繼承體系下的對(duì)象構(gòu)造:

            如果定義 T object 會(huì)發(fā)生什么

            1.member initialization list 中的data number 初始化操作會(huì)放到ctor 中并以聲明順序排列

            2.如果一個(gè)member沒有出現(xiàn)在 list 中但他有default constructor ,他必須被調(diào)用

            3.在那之前,如果class object有vptr 那么他必須設(shè)定初值指向正確vtable

            4.在那之前,任何上一層base class ctor 必須被調(diào)用,以base class 聲明順序,

            如果base class 列于 初始化列表中,參數(shù)被傳遞

            如果base class 不再列表,有默認(rèn)ctor 調(diào)用

            如果base class 是多重繼承下 第二或者后繼的base class this 指針調(diào)整

            5.在那之前virtual base class ctor 必須被調(diào)用 從左至右,由深到淺

            如果base class 列于 初始化列表中,參數(shù)被傳遞如果base class 不再列表,有默認(rèn)ctor 調(diào)用

            base class subobject 的offset 必須在執(zhí)行期可以被存取

            ··························

            虛擬繼承

            虛擬繼承比較具有特殊性,因?yàn)樗哂泄蚕硇浴T谶@樣一個(gè)繼承體系中

            image 

            Point 3d 和Vertex 虛擬繼承自Point  ,此時(shí)如果按照普通的ctor 規(guī)則。Vertex 的ctor 必須調(diào)用Point 的ctor ,然而 當(dāng)  Point3d 和 Vertex 同為Vertex3d的subobject時(shí),他們的調(diào)用一定不能發(fā)生(this指針的調(diào)整問(wèn)題) ,而只能有 Vertex3d才可以調(diào)用,同樣,PVertex構(gòu)造時(shí)候只能由他自己調(diào)用,也就是說(shuō)只有底層的class 完成構(gòu)建共享的subobject構(gòu)造。我們可以用一個(gè)most _derived參數(shù)傳給各個(gè)構(gòu)造函數(shù)以指示其是否調(diào)用共享部分的ctor。

            事實(shí)上我們發(fā)現(xiàn),只有當(dāng)object 是完整object時(shí)才會(huì)調(diào)用共享部分的ctor ,而部分subobject不會(huì)調(diào)用,從而得到新的策略,提供兩種構(gòu)造函數(shù)一種供完整的object 一種供subobject調(diào)用

             

            ~~~~vptr 初始化語(yǔ)義學(xué)~~~

            c++語(yǔ)言告訴我們:在Point3d ctor 調(diào)用size函數(shù),必須決議為 point3d  的size 而不是 pvertex的size,可以理解為在ctor和dtor 中虛函數(shù)決議為自己的函數(shù),他不虛啦~~~~~(實(shí)際上是因?yàn)樗麤]有變身完全)

            ctor 調(diào)用順序是從根源到末端,從內(nèi)而外。當(dāng)base class ctor 執(zhí)行時(shí)候 derived實(shí)體還沒有構(gòu)造出來(lái),在pvertex 構(gòu)造完整之前,pvertex并不是一個(gè)完整的對(duì)象,因此只有Point3d subobject構(gòu)造完畢,這意味著每一個(gè)pvertex base class ctor 調(diào)用時(shí)候,編譯器必須保證有適當(dāng)?shù)?size 函數(shù)實(shí)體來(lái)調(diào)用 how,so 決議為自己的size

            另一種方法是控制vptr,

            在base class ctor 調(diào)用之后,但是在程序員提供代碼或者initial list 之前,我們?cè)O(shè)定vptr ,也就是說(shuō)我們保證vptr指向剛剛構(gòu)造完畢的base clas subobject 對(duì)應(yīng)的vtable ,保證了它能夠調(diào)用正確的virtual func;

            所以對(duì)象構(gòu)造是這樣的過(guò)程:

            一個(gè) PVertex對(duì)象會(huì)先形成一個(gè) Point對(duì)象- >Point3d->Vertex->Vertex3d->PVertex

            ctor執(zhí)行算法:

            1.derived class ctor 中所有 virtual base class 和上一級(jí)base class 調(diào)用

            2. 上述完成vptr 初始化,指向相應(yīng)的vtable

            3.如果有member initial list 的haunted,將在ctor 展開,這些必須在vptr 設(shè)定后進(jìn)行,防止有virtual func 調(diào)用

            4.最后執(zhí)行程序員寫的代碼

            vptr 需要被設(shè)定的兩種情況:

            1.對(duì)象完整定義后

            2.當(dāng)一個(gè)subobject ctor 調(diào)用了一個(gè)虛擬函數(shù)時(shí)候(上面說(shuō)的很明白嘍)

            當(dāng)聲明一個(gè)PVertex對(duì)象時(shí)候,由于我們對(duì)base class ctor 的定義,其vptr 不需要每一個(gè)base class ctor 中設(shè)定,因此我們可以把ctor 分解成兩部分,一種完整的object  實(shí)體,一種subobject實(shí)體,subobject實(shí)體中vptr 設(shè)定可以是省略

            如果在class ctor initiallist 調(diào)用該class 虛擬函數(shù)安全么? 理論上安全,但是如果依賴未初始化member 不安全

            ``````````````````

            對(duì)象復(fù)制語(yǔ)義學(xué) object copy semantics

            我們有三種選擇:

            1 什么都不做,按默認(rèn)行為實(shí)施

            2 提供一個(gè)explicit copy assignment operator

            3 明確拒絕把一個(gè)class object 指定給一個(gè)class object (聲明一個(gè)private 的copy assignment operator)

            下列情況下,class 不表現(xiàn)出bitwise 的復(fù)制語(yǔ)義,也就是說(shuō)默認(rèn)的copy operator 是不夠的:(和之前介紹的一樣)

            1.class 有一個(gè)member object 而他有copy operator

            2.class的base class 有一個(gè)copy operator

            3.當(dāng)class 聲明virtual func時(shí) (由于要設(shè)定vptr when derived to base)

            4.class 繼承自 virtual base class

            同樣在這里也面臨ctor 中的問(wèn)題(虛擬繼承時(shí)),最底層的class 必須調(diào)用共享的base class的copy operator,一種策略是,并不壓制上面的class 調(diào)用自己的copy operator ,也就是說(shuō)允許了 共享部分的多重復(fù)制。另外一種方法是不要允許virtual base class 的copy 操作,甚至不要再任何virtual base class 中聲明數(shù)據(jù)~

            ··········析構(gòu)語(yǔ)義學(xué)semantics of Destruction

            如果class 沒有定義dtor 那么只有class 內(nèi)帶的member object 或者自己的base class 有dtor 時(shí)候,編譯器才會(huì)合成一個(gè)來(lái),否則dtor 被視為不需要了~~~(有virtual func 也不一定需要的)

            dtor 調(diào)用順序是這樣的:

            1 dtor  函數(shù)本身執(zhí)行,

            2 如果class 擁有member class object 而后者擁有dtor 那么他們會(huì)以相反的順序被調(diào)用

            3 如果object 帶有vptr ,現(xiàn)在被重新設(shè)定,指向適當(dāng)?shù)腷ase class vtble

            4 如果任意直接的nonvirtual base class 有dtor ,他們會(huì)以聲明的相反順序調(diào)用

            5 如果任意virtual base class 有destructor ,而當(dāng)前討論的這個(gè)class 是最低端的most –derived class 那么他們會(huì)以原來(lái)的構(gòu)造順序相反順序調(diào)用

             

            一個(gè)object 的生命周期結(jié)束于dtor 開始執(zhí)行時(shí),由于每一個(gè)base class dtor 輪番調(diào)用,所以一個(gè)derived object 實(shí)際上變成了一個(gè)個(gè)完整的objec ;一如 PVertex->Vertex3d->Vertex->Point3d->Point

            對(duì)象的蛻變會(huì)因?yàn)関ptr 重新設(shè)定受到影響(dtor中,程序員代碼之前),在程序中施行dtor的真正語(yǔ)義會(huì)在下一章具體表述~~

            #include <time.h>
            #include <iostream> 
            using namespace std;  
            class memberclass{
            public: 
                 memberclass(){cout<<"memberclass default ctor"<<endl;}
                memberclass(int x){cout<<"memberclass ctor with parameter"<<endl;}
                ~memberclass(){cout<<"memberclass dtor"<<endl;}
            };
            class Point{public: Point(){cout<<"point ctor"<<endl;test();}
            ~Point(){cout<<"point dtor"<<endl;test();}
            virtual void test(){cout<<"point test"<<endl;}
            };
            class Point3d:virtual public Point{public: Point3d(){cout<<"Point3d ctor"<<endl;test();}
            ~Point3d(){cout<<"Point3d dtor"<<endl;test();}
            virtual void test(){cout<<"Point3d test"<<endl;}};
            class vertex:virtual public Point{public: vertex(){cout<<"vertex ctor"<<endl;test();}
            ~vertex(){cout<<"vertex dtor"<<endl;test();}
            virtual void test(){cout<<"vertex test"<<endl;}
            };
            class vertex3d:public Point3d ,public vertex{public: 
            memberclass inside;
            vertex3d(){cout<<"vertex3d ctor"<<endl;test();}
            ~vertex3d(){cout<<"vertex3d dtor"<<endl;test();}
            virtual void test(){cout<<"vertex3d test"<<endl;}
            };
            class pvertex:public vertex3d{public: 
            memberclass inside;
            pvertex():inside(3){cout<<"pvertex ctor"<<endl;test();}
            ~pvertex(){cout<<"pvertex dtor"<<endl;test();}
            virtual void test(){cout<<"pvertex test"<<endl;}
            };
            int main(){ 
                pvertex* p = new pvertex;
                delete p;
                return 0;
            }
            輸出: guess it ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             

            point ctor
            point test
            Point3d ctor
            Point3d test
            vertex ctor
            vertex test
            memberclass default ctor
            vertex3d ctor
            vertex3d test
            memberclass ctor with parameter
            pvertex ctor
            pvertex test
            pvertex dtor
            pvertex test
            memberclass dtor
            vertex3d dtor
            vertex3d test
            memberclass dtor
            vertex dtor
            vertex test
            Point3d dtor
            Point3d test
            point dtor
            point test

             

            ---------------------------------------------------OVER--------------------------------------------------------

             
             
             
             
             
             
             
             
             
             
             
             
             
             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            posted @ 2010-03-12 11:06 rikisand 閱讀(1844) | 評(píng)論 (0)編輯 收藏

            Function 語(yǔ)義學(xué)

            ····member 的各種調(diào)用方式~

            1.非靜態(tài)成員函數(shù)·

            float Point3d::mangitude3d()const{…}

            會(huì)變成 float Point3d::magnitude(const Point3d* this){…}

            c++的準(zhǔn)則之一:非靜態(tài)成員函數(shù)至少必須同一般的nonmember function 有相同的效率

            名稱的特殊處理:(name mangling)一般member名稱前面都會(huì)加上class名稱,形成獨(dú)一無(wú)二的命名,class Bar {public :int ival;} 可能變成 ival_3Bar ;3應(yīng)該是bar的長(zhǎng)度了。

            這樣可以防止繼承體系中兩個(gè)類定義同樣名字的變量~

            如果使用extern “C” 就可以壓制nonmember 的mangling 效果

            2.虛擬成員函數(shù)·

            如果normalize 是虛擬函數(shù) 他會(huì)被翻譯成:

            (*ptr->vptr[1])(ptr); 第二個(gè)ptr是this指針

            類似的magnitude 會(huì)變成

            (*this->vptr[2])(this);

            而magnitude是在normalize之后調(diào)用的因此此時(shí)已經(jīng)確定this指向的是Point3d 因此可以直接調(diào)用Point3d::magnitude()更有效率

            如果用一個(gè)對(duì)象調(diào)用一個(gè)虛擬函數(shù)應(yīng)該把它當(dāng)做正常函數(shù)來(lái)對(duì)待,因?yàn)榭梢源_定對(duì)象類型直接調(diào)用相應(yīng)的函數(shù)即可,在這種情況下,虛擬函數(shù)也可以inline 來(lái)提高效率了~~~

            3.靜態(tài)成員函數(shù)

            class A{
            public:
                static int a;
                static int geta(){return a;}
            };
            int A::a=33333;
            int main(){
                cout<< ((A*)0)->geta()<<endl;
            }

            static的主要特征是他沒有this 指針,這樣導(dǎo)致“

            他不能直接存取其class中的nonstatic members

            他不能被聲明為const volatile 或者virtual

            他不需要經(jīng)由class object 才被調(diào)用 雖然大部分情況是這樣調(diào)用的

            如果取一個(gè)static member func 地址則得到的是他在內(nèi)存中的真正地址,而且得到的是一個(gè)函數(shù)指針,而不是一個(gè)指向class member 函數(shù)的指針

             

            ····虛擬成員函數(shù)

            為了支持virtual func 機(jī)制,必須首先能夠?qū)Χ鄳B(tài)對(duì)象由某種形式的運(yùn)行期類型判斷方法

            c++中多態(tài)表示:以一個(gè)public blase class 指針或者引用 尋址出一個(gè)derived class object 的意思

            識(shí)別出哪個(gè)類需要支持多態(tài)只要看他是否有任何的virtual func

            ~~~單一繼承

            vtable中每一個(gè)virtual func(包括pure func)都被指派一個(gè)固定的索引值,這個(gè)索引在整個(gè)繼承體系中保持與特定的virtual function 的關(guān)聯(lián)

             

            當(dāng)一個(gè)class 繼承自上一個(gè)class時(shí)候

            1.可以繼承base class 聲明的virtual func ,這樣該函數(shù)實(shí)體的地址會(huì)被拷貝到他的vtable相對(duì)應(yīng)的slot 中,位置x不變 這樣調(diào)用時(shí)候 ptr->func();會(huì)翻譯成 (*ptr->vtbl[x])func(ptr) ;而不用管ptr 到底是一個(gè)base 還是一個(gè)derived

            2.他可以使用自己的函數(shù)實(shí)體,表示他自己的函數(shù)地址必須放在相應(yīng)的位置x處 ,跟上面的例子一樣

            3.可以加入新的virtual 函數(shù),這時(shí)候vtbl 會(huì)變大

            ~~~多重繼承呢

            多重繼承時(shí)候 例如 Derived public 自 Base1,Base2

            Base2 *pbase2 = new Derived; 新的Derived必須調(diào)整

            Base2 *pbase2 = tmp?tmp+sizeof(Base1):0;

            當(dāng)程序員刪除pbase2指向的對(duì)象時(shí)指針必須再一次調(diào)整。上述的調(diào)整并不能在編譯時(shí)期設(shè)定,因?yàn)閜base2指向的對(duì)象只有在執(zhí)行期才能確定。

            同樣道理,pbase2 如果要調(diào)用函數(shù)的話,調(diào)用操作會(huì)引發(fā)必要的指針調(diào)整,也必須在執(zhí)行期調(diào)整。

            Bjarne采用擴(kuò)充vtable 每一項(xiàng)記錄調(diào)整this指針的信息,但浪費(fèi),因?yàn)榇蟛糠植恍枰{(diào)整

            Thunk技術(shù)是用一段匯編實(shí)現(xiàn)調(diào)整this指針以及跳到virtual func的過(guò)程

            調(diào)整this指針的第二個(gè)負(fù)擔(dān)是:如果由derved class 調(diào)用,或者由第二個(gè)base class 調(diào)用,同一個(gè)函數(shù)可能在virtual table 對(duì)應(yīng)多個(gè)slots

            pbase1 和derived 的vtable可以合并,他們用同樣的slots 偏移,里面可以放真正的地址,而pbase2 需要調(diào)整this指針,其vtabl 相應(yīng)的地址放的是相應(yīng)的thunk地址。

            可以看到”:

            1.如果通過(guò)指向第二個(gè)base class 指針調(diào)用derived的func ptr 需要調(diào)整

            2.如果通過(guò)指向derived指針調(diào)用從第二個(gè)繼承來(lái)的func 需調(diào)整

            3.如果允許virtual func 返回類型有所變化,可能base 可能derived,也需要調(diào)整this

            Microsoft 用address point 策略,即將用來(lái)改寫別人的函數(shù),期待獲得的參數(shù)(this)是引入該class 的地址,這就是函數(shù)的address class(~~不了啊~~)

            ~~~虛擬繼承下的virtual func

              即便只有一個(gè)base clas 它的布局轉(zhuǎn)換也需要this 指針的調(diào)整,相當(dāng)復(fù)雜~~~

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

            double Point::x();

            可以定義指向成員函數(shù)的指針

            double (Point::* pmf)()=&Point::x;

            調(diào)用可以  (origin.*pmf)() 或者 ptr->*pmf();

            如果是虛擬函數(shù)的指針呢??

            Point* ptr= new Point3d;

            如果x是一個(gè)虛擬函數(shù)

            (ptr->*pmf)();仍然是Point3d::x()被調(diào)用么?

            答案~~是的

            因?yàn)槿〉锰摂M函數(shù)的地址其實(shí)取得的是虛擬函數(shù)的offset值

            調(diào)用會(huì)變成  (*ptr->vtbl[(int)pmf])(ptr);

            class A{
            public:
                static int a;
                static int geta()  {return a;}  //靜態(tài)并不能作為重載條件
                int geta(int x){
                    return a;
                }
                 int  geta( int  a)const{} // const成員函數(shù) ,可以作為重載條件
            };
            int A::a=33333;
            int main(){
                A a;
                cout<< ((A*)0)->geta()<<endl;//靜態(tài)成員函數(shù)的一種調(diào)用方法 ((A*)0)->geta()
                int(*p)()= &A::geta;
                cout<<(*p)()<<endl;
                int (A::* pgeta)(int a) = &A::geta;
                cout<<(a.*pgeta)(3)<<endl;
            }

            輸出均為33333 

            多重繼承下呢????

            Microsoft提供了3種解決方法:

            一種:?jiǎn)我焕^承的情況(帶vcall thunk地址或者函數(shù)地址)

            2多重繼承 帶有faddr 和delta

            虛擬繼承 帶有四個(gè)members

            (·····具體以后再查吧)

            ----------

            inline members

            真正的inline 函數(shù)擴(kuò)展是在調(diào)用的那一個(gè)點(diǎn)上,這回帶來(lái)參數(shù)的求值操作以及暫時(shí)性對(duì)象的管理

             

            形式參數(shù) formal arguments

            在inline 期間 每一個(gè)形式參數(shù)都會(huì)被相應(yīng)的實(shí)際參數(shù)取代,副作用是,不可以只是簡(jiǎn)單的一一封塞程序中出現(xiàn)的每一個(gè)形式參數(shù),因?yàn)檫@將導(dǎo)致對(duì)于實(shí)際參數(shù)的多次求值操作,可能產(chǎn)生 帶來(lái)副作用的 實(shí)際參數(shù),通常這需要嵌入實(shí)際對(duì)象的~~~~

            所以,如果實(shí)際參數(shù)是常量,那么我們可以直接綁定,如果不是常量也沒有副作用,我們直接代替,否則~~~暫時(shí)對(duì)象會(huì)需要的~·

            例如:

            inline int min(int i,int j) { return i<j ? i:j ;}

            minval = min(val1,val2);

            minval = min(11,12);

            minval = min (foo(),bar()+1);

             

            這會(huì)擴(kuò)展成: minval = val1<val2 ? val1?val2;

            minval = 11;( 常量哦)

            int t1,t2; minval =(t1 = foo()), (t2=bar()+1),t1<t2?t1:t2;

            如果我們改變函數(shù)定義

            {int minval = i<j?i:j; return minval;}

            如下調(diào)用{int minval ; minval = min(val1,val2);}

            為了維護(hù)局部變量可能會(huì)變成:

            { int m_lv_minval; minval=(__min_lv_minval=val1<val2?val1:val2),min_lv_minval;}

            一般而言,inline 函數(shù)的每一個(gè)局部變量都必須放在函數(shù)調(diào)用的一個(gè)封閉區(qū)段中,擁有一個(gè)獨(dú)一無(wú)二的名字,如果inline函數(shù)以單一表達(dá)式擴(kuò)展多次,那么每次擴(kuò)展都需要自己的一組局部變量。如果inline 函數(shù)可以以分離的多個(gè)式子被擴(kuò)展多次,那么只需要一組局部變量就可以重復(fù)使用,因?yàn)樗麄儽环忾]在自己的scope中:

            例如 minval = min(val1,val2) + min(foo(),foo()+1) ;

            擴(kuò)展 int __min_lv_minval_0,__min_lv_minval_1,t1,t2;

            minval = ((__min_lv_minval_0 = val1<val2?val1:val2),__min_lv_minval_0)+…);

            參數(shù)帶有副作用或者是以一個(gè)單一表達(dá)式做多重調(diào)用,或者是在inline 函數(shù)內(nèi)部有多個(gè)局部變量

            都會(huì)產(chǎn)生局部變量,要小心對(duì)待

            --------------------結(jié)束線哦~~~~~~··----------------------

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            posted @ 2010-03-09 22:20 rikisand 閱讀(1574) | 評(píng)論 (1)編輯 收藏

            DATA 語(yǔ)義學(xué)

            這段代碼輸出什么?

            #include <iostream>
            using namespace std;
            class A{ public:A(){ac='s';}private:char ac;};
            class B:virtual public A{public:B(){a='e';}char a; };
            class C:virtual public A{ };
            class D:public  B,public  C
            {
            public:
                D():A(),B(),C(){b=13;}
                int b;
            };
            int main(){
                D d;
                cout<<"sizeof(A)="<<sizeof(A)<<endl;
                cout<<"sizeof(B)="<<sizeof(B)<<endl;
                cout<<"sizeof(C)="<<sizeof(C)<<endl;
                cout<<"sizeof(D)="<<sizeof(D)<<endl;
                cout<<endl;
                cout<<"address of A's subobject in d"<<(A*)&d<<endl;
                cout<<"address of B's subobject in d"<<(B*)&d<<endl;
                cout<<"address of C's subobject in d"<<(C*)&d<<endl;
                cout<<"address of D's subobject in d"<<(D*)&d<<endl;
                cout<<endl;
                int* p = (int*)(*((int*)&d));
                cout<<"address of b's virtual base table="<<p<<endl;
                cout<<"first member in b's virtual base table="<<*p<<endl;
                cout<<"second member in b's virtual base table="<<*(p+1)<<endl;
                cout<<"third member in b's virtual base table="<<*(p+2)<<endl;
                cout<<endl; 
                p= (int*)*((int*)((C*)&d));
                cout<<"address of c's virtual base class table= "<<p<<endl;
                cout<<"first member in c's virtual base table="<< *p<<endl;
                cout<<"second member in c's virtual base table="<<*(p+1)<<endl;
                cout<<"third member in c's virtual base table="<<*(p+2)<<endl;
                char *pchar= (char*)(&d)+4; //char型加4   注意A中的ac其實(shí)是私有變量,B不應(yīng)該可以訪問(wèn)到的,實(shí)際上通過(guò)強(qiáng)制轉(zhuǎn)換可以非法觸及她-。-
                cout<<*pchar<<endl;
                cout<<*(pchar+12)<<endl;
                B b;
                int *pint =(int*)(&b)+1; //int型+1 
                cout<<*((char*)(pint))<<endl;
                pint = (int*)(&d)+3;
                cout<<*(pint)<<endl;
            }

             

            結(jié)果是:

            sizeof(A)=1
            sizeof(B)=9
            sizeof(C)=5
            sizeof(D)=17

            address of A's subobject in d0012FF74
            address of B's subobject in d0012FF64
            address of C's subobject in d0012FF6C
            address of D's subobject in d0012FF64

            address of b's virtual base table=00403350
            first member in b's virtual base table=0
            second member in b's virtual base table=16
            third member in b's virtual base table=0

            address of c's virtual base class table= 00403358
            first member in c's virtual base table=0
            second member in c's virtual base table=8
            third member in c's virtual base table=0
            e
            s
            e
            13

             

            1.語(yǔ)言本身造成的負(fù)擔(dān):如virtual baseclass

            2.對(duì)齊造成的負(fù)擔(dān)(對(duì)齊會(huì)單獨(dú)開題討論)

            3.編譯器優(yōu)化處理 A雖然是空的 but 為了讓A的兩個(gè)object在內(nèi)存中得到不同的地址,編譯器給他加上了一個(gè)byte,但是B和C都沒有這一個(gè)byte呢?這是編譯器做了優(yōu)化,允許省掉這一個(gè)byte

            看上面的代碼”:

            環(huán)境是vs2008 對(duì)齊設(shè)置為4字節(jié)

            A的大小為1 因?yàn)橛幸粋€(gè)char 沒有對(duì)齊因?yàn)椴皇墙Y(jié)構(gòu)型對(duì)象,如果A沒有這個(gè)char大小依然是1的

            B的大小為9 首先開始是它的virtual base class ptr,4個(gè)字節(jié)的指針,然后是他自己的member 1個(gè)char 此時(shí)為了保證其對(duì)象完整性編譯器對(duì)齊到4字節(jié)處也就是在第九個(gè)字節(jié)內(nèi)放入基類的member

            這樣如果我們把A=B B賦給A,傳輸可以從整4字節(jié)開始割出A即可

            C的大小是5 沒什么好解釋

            D的大小是17首先是B的8字節(jié)(4字節(jié)vbptr+1字節(jié)char member+3字節(jié)對(duì)齊)然后是C的4字節(jié)vbptr,然后是自己的member4字節(jié)最后是1字節(jié)的base class member,可以看到B和C的base class table中的項(xiàng)都是自己位置與這個(gè)member的offset 值

             

            不同編譯器可能結(jié)果不同的,因?yàn)閏++ standard 并沒有強(qiáng)制規(guī)定 base class subobjects的順序等

             

            data member 是程序執(zhí)行過(guò)程中的某種狀態(tài):

            static data member 是整個(gè)class 的狀態(tài)

            non-static data member 是個(gè)別class-object 的狀態(tài)

            c++對(duì)象盡量以空間優(yōu)化和存取速度的考慮來(lái)表現(xiàn)non-static members ,并且和c的struct 數(shù)據(jù)配置的兼容性。

            static data member 放在程序的一個(gè)global data segment 中,不會(huì)影響個(gè)別的class-object 大小

            ,在class沒有object 時(shí)已經(jīng)存在,但是template中有些不同

             

            -----DATA member 的綁定

            始終把nested type 聲明 放在class 起始處,argument list 中的名稱會(huì)在第一次遇到時(shí)候被適當(dāng)?shù)臎Q議完成,因此extern 和nested type names 之間的非直覺綁定操作還是會(huì)發(fā)生。

            ---- DATA 的存取

            Point3d origin,*pt=&origin;

            origin.x = 0.0; 和 pt->x=0.0 ; 有什么區(qū)別么??

            如果x是靜態(tài)data member 則完全沒有區(qū)別 因?yàn)樗麄兌荚赿ata segment 中和object無(wú)關(guān)

            ~nonstatic data member---------

            如果point3d不包含虛擬繼承那么沒有差異

            否則我們不能確定pt中必然指向哪一種因此不能在編譯器確定offset需要一些運(yùn)行時(shí)候的計(jì)算抉擇,而origin則不同一定是某一個(gè)類型的所以沒有問(wèn)題

            多繼承或者單繼承都不會(huì)帶來(lái)訪問(wèn)上的影響,因?yàn)樗麄兌伎梢韵騝的結(jié)構(gòu)體那樣在編譯時(shí)期確定各個(gè)member的offset。即使是多繼承pt指向第二個(gè)baseclass的data,由于member的位置在編譯時(shí)期就已經(jīng)固定了,因此存取member只是一個(gè)offset運(yùn)算,像單一繼承那樣簡(jiǎn)單,不管是指針,reference或者object都一樣

            只有virtual base class 會(huì)帶來(lái)一些損耗,因?yàn)樗沟脤?duì)象模型變得復(fù)雜了

            如果我們?cè)谝粋€(gè)類中加入了virtual func 會(huì)發(fā)生什么~~~

            1. 會(huì)產(chǎn)生一個(gè)virtual table,存放每一個(gè)virtual func地址以及rtti支持的type_info

            2.class object 內(nèi)都加入一個(gè)vptr,提供執(zhí)行期的鏈接

            3.加強(qiáng)ctor 設(shè)定vpr初值

            4.加強(qiáng)dtor 消除vptr 從dirived class 到 base class

             

            虛擬繼承:

            他必須支持某種形式的“shared subobject”繼承

            那么一個(gè)object會(huì)分成一個(gè)不變局部和一個(gè)共享局部的數(shù)據(jù),共享局部就是virtual base class subobject,他的位置會(huì)因?yàn)槊看闻缮僮靼l(fā)生變化(例如一個(gè)virtual base class subobject的位置在不同級(jí)別的繼承體系中的位置是不確定的,不像多繼承單繼承那樣有固定的offset),所以他只能間接存取,因此產(chǎn)生了效率缺損.(間接是指的是他只能首先讀出他的指針,然后根據(jù)指針的內(nèi)容取到他)

            所以在虛擬繼承基類中最好不要有data member

            -------指向DATAmember 的指針

            #include <iostream>
            using namespace std;

            class  A
            {
            public:

                int a;
            };
            int main(){
                int  A::* p=&A::a;
                cout<<p<<endl;
            }

            輸出 1

            因?yàn)闉榱朔乐?amp;A::a和 int A::*a = 0;一樣把他加了1。

             

            虛擬繼承帶來(lái)的主要沖擊是,妨礙了優(yōu)化的有效性,因?yàn)槊恳粚犹摂M繼承都帶來(lái)了一個(gè)額外層次的間接性,在編譯器中存取 類似point::x的操作pb.*bx

            會(huì)被轉(zhuǎn)化為 &pb->vbcPoint+bx

            而不是轉(zhuǎn)換成 &pB +bx

            額外的間接性會(huì)降低“把所有操作都搬移到緩存器中執(zhí)行”優(yōu)化能力,也就是降低了局部性~

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            posted @ 2010-03-07 16:39 rikisand 閱讀(305) | 評(píng)論 (0)編輯 收藏

            構(gòu)造函數(shù)語(yǔ)義學(xué)

            --了解編譯器在構(gòu)造對(duì)象時(shí)背著我們干了什么勾當(dāng)

            Default Ctor 在需要的時(shí)候被構(gòu)建出來(lái)~

            什么需要? 是編譯器需要而不是程序的需要,所以不要期望編譯器生成的Ctor會(huì)幫我們把我們的成員變量初始化為零。那是程序的需要,也就是我們程序員的任務(wù)····

            例如:

                  class Foo(public:int val;);

                  void foo_bar(){Foo bar; if(bar.val)dosth;} 不要期望編譯器幫我們初始化它為0。只有g(shù)lobal變量會(huì)初始化為0,初始化val這樣的變量需要我們自己寫代碼的~~

            Default Ctor分為trival(沒用的)和non-trival的,下面討論什么時(shí)候編譯器需要ctor 也就是有用的ctor,這時(shí)候如果我們沒有提供一個(gè)默認(rèn)的default ctor 它會(huì)幫我們合成一個(gè)的~~

            1.帶有Default Ctor 的member class object

            很好理解,既然內(nèi)部成員含有default ctor 那么我們創(chuàng)建新的對(duì)象時(shí)需要調(diào)用它,而我們并木有調(diào)用它的函數(shù),編譯器自然會(huì)幫我們提供一個(gè)。如果我們提供了default ctor ,那么編譯器會(huì)在我們提供的函數(shù)加入調(diào)用必須調(diào)用的member class 的default ctor。同樣的在每一個(gè)ctor里面都將在用戶寫的代碼之前調(diào)用需要調(diào)用的default ctor -------看例子:

            class Dopey();class Sneezy{public:Sneezy(int val);Sneezy();};class Bashful{public:BashFul();};

            class Snow_white{public: Dopey dopey;Sneezy sneezy;Bashful bashful;private:int muble};

            如果Snow_white沒有定義default ctor 那么編譯器會(huì)創(chuàng)建一個(gè),并在其中依照聲明順序依次調(diào)用dopey sneezy bashful的default ctor 然而如果:

            Snow_white::Snow_white():sneezy(1024){muble=2045;}

            編譯器會(huì)擴(kuò)張為

            Snow_white::Snow_white():sneezy(1024){

              dopey.Dopey::Dopey();

              sneezy.Sneezy::Sneezy(1024);

              bashful.Bashful::Bashful();

              /////////   explicit user code

              muble = 2048;

            }

            2.派生自帶有Default ctor 的 base class

            同樣道理如果父類有Default ctor 子類當(dāng)然要調(diào)用,編譯器會(huì)為想上面一樣為我們加上

            3.含有virtual functions的Class

            創(chuàng)建object 時(shí)候需要ctor 來(lái)設(shè)置好vptr

            4.帶有virtual base class

            virtual base 實(shí)現(xiàn)方法各有不同,然而共同點(diǎn)是必須是virtual base class 在其每一個(gè)derived class中的位置能夠與執(zhí)行期準(zhǔn)備妥當(dāng)

            X A B C 菱形繼承

            void foo(const A*pa){pa->i=1024;}

            foo(new A);foo(new C);

            知道pa 后 i在對(duì)象中的位置并不是固定的,而是在運(yùn)行時(shí)真正確定pa指向什么對(duì)象A還是C才能確定的,因此需要設(shè)定一個(gè)指向基類subobject的指針,所以需要ctor工作了

            OK:

            1.任何class 如果沒有定義Default ctor 就會(huì)被合成一個(gè)

            2.合成出來(lái)的Default ctor 會(huì)明確設(shè)定每一個(gè)data member值

            錯(cuò)的很easy了~

            -------------------------------------------------------------------------------

            再來(lái)看 Copy Ctor:

            copy ctor 負(fù)責(zé)用另一個(gè)對(duì)象初始化一個(gè)對(duì)象

            operator = 負(fù)責(zé)用另一個(gè)對(duì)象給一個(gè)對(duì)象賦值

            直接賦值時(shí),傳參時(shí),返回時(shí)可能調(diào)用Copy ctor

            Default member initialization~~~

            也就是memberwise 的initialization

            他會(huì)把每一個(gè)data member (內(nèi)建的或者派生的)從一個(gè)object 拷貝到另一個(gè)object中去

            如果object允許bitwise的拷貝那么編譯器就不用生成一個(gè)nontrival的default copy ctor

            什么時(shí)候不可以呢~

            1 內(nèi)含一個(gè)member object 而后者含有copy constructor (聲明或者合成的)

            2  繼承一個(gè)base class 后者有copy ctor

            3  含有virtual func

            4  派生自一個(gè)繼承鏈,其中有virtual base class

            1和2 中編譯器會(huì)把member 或者baseclass copy ctor 調(diào)用安插在合成的copy ctor 中

            3 中:

            如果兩個(gè)同樣類型的object賦值時(shí),沒有問(wèn)題因?yàn)樗麄兊膙ptr相同

            但是考慮子類賦值給父類,此時(shí)vptr需要更改,那么此時(shí)不具有bitwise特性,因此需要編譯器來(lái)加入語(yǔ)句正確更新vptr

            4中:

            每個(gè)編譯器都承諾必須讓derived class 中的virtual base class object 在執(zhí)行期間準(zhǔn)備妥當(dāng),維護(hù)位置完整性是編譯器的責(zé)任。bitwise copy 有可能會(huì)破壞這個(gè)位置所以編譯器需要在自己合成的copy ctor 中作出仲裁

            同樣問(wèn)題發(fā)生在繼承體系中子類向父類賦值時(shí),由于對(duì)象模型問(wèn)題,直接bitwise復(fù)制可能會(huì)導(dǎo)致base class object 的破壞(后面章節(jié)會(huì)有討論)

            --------------------------------------------------------------------------------

            程序轉(zhuǎn)化語(yǔ)義學(xué):

            X x0;

            void foo(){X x1(x0); X x2=x0; X x3=X(x0);}

            轉(zhuǎn)化:重寫定義,初始化操作會(huì)被剝除   copy constructor 調(diào)用會(huì)被安插

            void foo(){ X x1;X x2; X x3;  x1.X::X(x0); x2.X::X(x0); x3.X::X(x0);}

             

            參數(shù)的初始化:

            一種策略導(dǎo)入暫時(shí)的object

            void foo(X x0);X xx; foo(xx);

            轉(zhuǎn)化:

            X _tmp;

            _tmp.X::X(x0); foo(_tmp);

            foo變成 void foo(X& x0);

            另一種是拷貝構(gòu)建:

            把實(shí)際參數(shù)直接建構(gòu)造應(yīng)該在的位置上,函數(shù)返回時(shí)局部對(duì)象的destructor 會(huì)執(zhí)行

            也就是說(shuō)把x0建構(gòu)在foo 的函數(shù)執(zhí)行的地方

             

            返回值的初始化:

            X bar(){

                X xx;

                return xx;

            }

            返回值如何從局部對(duì)象xx拷貝而來(lái)呢?

            一種方法:1.加上額外參數(shù),類型是class object的reference,這個(gè)參數(shù)用來(lái)放置被拷貝建構(gòu)的返回值 (注意拷貝建構(gòu)也就是說(shuō)他被放在應(yīng)該在的位置,也就是說(shuō)不是局部變量了)

            2.return 指令之前安插一個(gè)copy constructor 調(diào)用操作 ,以便將傳回的object 內(nèi)容當(dāng)做上述參數(shù)的初值

            so 上面的程序變成了:

            void bar(X& _result){

              X xx;

              xx.X::X(); //編譯器產(chǎn)生的default ctor 調(diào)用

              _result.X::X(xx);//編譯器產(chǎn)生的copy ctor 調(diào)用

              return ;

            }

            現(xiàn)在編譯器必須轉(zhuǎn)換bar的調(diào)用操作 X xx=bar();轉(zhuǎn)換成 X xx; bar(xx); // 注意不用copy ctor了直接操作在xx上了 如果編譯器做了優(yōu)化 這就是named return value 優(yōu)化

            而:

            bar.memfunc();//bar()傳回的X 調(diào)用成員函數(shù)

            變成:

            X _tmp;  (bar(_tmp),_tmp).memfunc();

            同樣道理函數(shù)指針 X(*pf)(); 變成 void (*pf)(X&);

            使用者的優(yōu)化:

            X bar(const T& y,const T& z){

                X xx;

                通過(guò) y z 計(jì)算xx

               return xx;

            }

            這種情況下要iuxx被memberwise拷貝到編譯器產(chǎn)生的_result中,

            如果定義 ctor來(lái)利用yz計(jì)算xx則:

            X bar(const T& y,const T& z){

                 return X(y,z);

            }

            變成:

            bar(X& result){

                 result . X::X(y,z);

                 return;

            }

            無(wú)需copy ctor了

             

            編譯器的優(yōu)化:

            如上所述bar中返回了具名數(shù)值 named value,因此編譯器有可能自己優(yōu)化,方法是以result取代named return value

            bar(X& result){_result.X::X(); 直接處理result 并不處理變量xx然后復(fù)制給result 這樣就優(yōu)化了}

            這個(gè)優(yōu)化的激活需要class提供一個(gè)copy ctor~~~~~~

             

            Copy ctor 要不要:

            如果一個(gè)class 符合bitwise的要求那么此時(shí)member wise 的拷貝已經(jīng)高效簡(jiǎn)潔 無(wú)需加入了

            但是如果class需要大量的member wise 初始化操作,如用傳值方式返回objects,如果是這樣提供一個(gè)copy ctor 可以激活nRV named return value 優(yōu)化,這就很合理了

             

            成員們的初始化過(guò)程:

            什么時(shí)候必須用初始化列表:

            1.初始化一個(gè)reference時(shí) 2.初始化一個(gè)const member 3.調(diào)用父類ctor而且有參數(shù)時(shí)4調(diào)用member class ctor 有參數(shù)

            其他情況呢:

            class word{string name;int cnt;public: name=0;cnt=0;}

            編譯器可能這么做:

            word::word{

                name.String::string();調(diào)用string的default ctor

                string tmp=string(0);

            _name.string::operator=(tmp);

            tmp.string::~string();

            cnt=0;

            }

            顯然name放到初始化列表會(huì)更有效率 ,會(huì)變成

            name.String::String(0);

            而cnt這種內(nèi)建類型則沒有關(guān)系,放不放到初始化列表沒有效率上的差別

            初始化列表究竟讓編譯器做了什么????

            編譯器會(huì)一個(gè)個(gè)操作list中的式子,以適當(dāng)次序在ctor內(nèi)安插初始化操作在任何explicit user code 之前。

            注意的地方:

                   list中的次序是按照members聲明次序決定而不是list 中的排列順序決定。

            例如:class x{int i;int j; X(int val):j(val),i(j)}

            錯(cuò)了 i先聲明則i首先賦予val 然后用未初始化的j賦給i。。。

            可以這樣X::X(int val):j(val){i=j;}

            由于會(huì)安插在explicit code 之前 所以沒問(wèn)題 會(huì)變成 j=val;   i=j;

            可否用member functions 初始化成員??

            答案是可以的,因?yàn)楹蚾bjects相關(guān)的this指針已經(jīng)構(gòu)建妥當(dāng),只是要注意函數(shù)調(diào)用的member是否已經(jīng)構(gòu)建妥當(dāng)了即可

            ------         -  -  - ----------       --       --疲憊的結(jié)束線-          - -  -     - --            -           -----

            name return value TEST:

            ~~~~1 cl /od 不開優(yōu)化

            #include <iostream>
            using namespace std;
            class RVO
            {
            public:

                RVO(){printf("I am in constructor\n");}
                RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
                ~RVO(){printf ("I am in destructor\n");}
                int mem_var;      
            };
            RVO MyMethod (int i)
            {
                RVO rvo1;
                rvo1.mem_var = i;
                return (rvo1);
            }
            int main()
            {
                RVO rvo;rvo=MyMethod(5);
            }

            輸出:

            I am in constructor         //rvo 創(chuàng)建
            I am in constructor         // rvo1創(chuàng)建
            I am in copy constructor // rvo1賦值給hiddern
            I am in destructor          // rvo1解構(gòu)
            I am in destructor          // hiddern解構(gòu)
            I am in destructor         //  rvo 解構(gòu)

            A MyMethod (A &_hiddenArg, B &var)
            {
               A retVal;
               retVal.A::A(); // constructor for retVal
               retVal.member = var.value + bar(var);
               _hiddenArg.A::A(retVal);  // the copy constructor for A
               return;
            retVal.A::~A();  // destructor for retVal
            
            }
            A MyMethod(A &_hiddenArg, B &var)
            {
               _hiddenArg.A::A();
               _hiddenArg.member = var.value + bar(var);
               Return
            }
            ~~~~2 cl /o2 代碼同上
            output

            I am in constructor  //rvo創(chuàng)建
            I am in constructor  //hiddern 創(chuàng)建
            I am in destructor   //hiddern 解構(gòu)
            I am in destructor   //rvo解構(gòu)

            我不明白的是hiddern 怎么傳給rvo ,我猜可能是編譯器按照bitwise的復(fù)制方式進(jìn)行的,此時(shí)編譯器并沒有直接建構(gòu)結(jié)果于rvo上 ,看看下面的試驗(yàn):

            注:明白了, 結(jié)果直接建構(gòu)在hiddern,然后通過(guò)operator = 傳給rvo 。沒有copy ctor因?yàn)榭截悩?gòu)造函數(shù)是負(fù)責(zé)初始化的,而operator = 才是用來(lái)賦值的.

            經(jīng)過(guò)代碼證明是對(duì)的,如果重載賦值運(yùn)算符 輸出變成:

            I am in constructor  //rvo創(chuàng)建
            I am in constructor  //hiddern 創(chuàng)建

            I am in operator =   //賦值操作~~
            I am in destructor   //hiddern 解構(gòu)
            I am in destructor   //rvo解構(gòu)

            ~~~~3 cl /od

            #include <iostream>
            using namespace std;
            class RVO
            {
            public:

                RVO(){printf("I am in constructor\n");}
                RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
                ~RVO(){printf ("I am in destructor\n");}
                int mem_var;      
            };
            RVO MyMethod (int i)
            {
                RVO rvo1;
                rvo1.mem_var = i;
                return (rvo1);
            }
            void abc(RVO& i){
            }
            int main()
            {
                RVO rvo=MyMethod(5);  //此時(shí)定義和賦值放到了一個(gè)表達(dá)式子
                return 0;
            }

            output:

            I am in constructor           // rvo1 創(chuàng)建 注意 跟上面的第一種情況下的hiddern一樣 rvo并沒有調(diào)用ctor
            I am in copy constructor   // rvo1 拷貝構(gòu)造給rvo 此時(shí)沒有hiddern了 直接構(gòu)建rvo了
            I am in destructor            // rvo1 析構(gòu)
            I am in destructor            // rvo1 解構(gòu)

            ~~~~3 cl /o2  再來(lái)~~~~ NRV出馬

            I am in constructor           // rvo構(gòu)建了 
            I am in destructor            // rvo析構(gòu)了

            此時(shí) mymethod中的一切都直接反映在rvo身上

            ok~~~~~4個(gè)代碼完全一樣構(gòu)造析構(gòu)拷貝函數(shù)個(gè)數(shù)由2-6不等~~~over~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            posted @ 2010-03-03 22:23 rikisand 閱讀(1384) | 評(píng)論 (4)編輯 收藏

            寒假基本讀了一遍,現(xiàn)在再讀一遍,做下筆記。

            筆記當(dāng)做的精煉而有意義,而后回顧可知其意,回其味,方有成效

            ---------------------------------------------------------------------------------

            C語(yǔ)言:數(shù)據(jù)和操縱數(shù)據(jù)的方法分開聲明,方法被寫成函數(shù)處理外部數(shù)據(jù)

            C++:數(shù)據(jù)和方法的組合封裝ADT 更安全 支持繼承 清晰

            封裝在空間上的代價(jià):

            如果僅僅是普通繼承則并沒有什么代價(jià),因?yàn)槊總€(gè)數(shù)據(jù)成員直接內(nèi)涵在每一個(gè)class對(duì)象中,和c中的struct一樣,而member function如果不是inline的則只會(huì)產(chǎn)生一個(gè)實(shí)體,而inline會(huì)在每一個(gè)使用的模塊產(chǎn)生一個(gè)函數(shù)實(shí)體

            c++在空間和存取效率的代價(jià)主要由virtual帶來(lái):

            virtual function 機(jī)制: vtable vptr 執(zhí)行期綁定

            virtual class 機(jī)制:

            還有多重繼承下的額外負(fù)擔(dān)(子類轉(zhuǎn)換成第二個(gè)父類的時(shí)候)

            ----------------------------------------------------------------------

            c++對(duì)象模型:也就是我們?cè)趺丛趦?nèi)存中安排各個(gè)數(shù)據(jù)成員以及成員函數(shù)

            1.簡(jiǎn)單對(duì)象模型~ 對(duì)象由各個(gè)slot組成,每一個(gè)slot包含指針,指向各個(gè)成員

            2.表格驅(qū)動(dòng)模型~ 對(duì)象含有兩個(gè)指針,一個(gè)指向成員數(shù)據(jù)表,另一個(gè)指向成員函數(shù)表

            3.c++實(shí)際對(duì)象模型~ 非靜態(tài)數(shù)據(jù)被放在對(duì)象內(nèi),而靜態(tài)數(shù)據(jù)成員放在對(duì)象外(顯然啊,他屬于類),成員函數(shù)

              不管靜態(tài)或者非靜態(tài)都放在對(duì)象外面

              Virtual functions :每個(gè)class產(chǎn)生一堆指向vitrualfunctions的指針,放在vtables中,每個(gè)對(duì)象

              含有vptr 指向vtable vptr的設(shè)定,重置均有class的ctor,dtor,copy assignment 運(yùn)算符自動(dòng)完成

              優(yōu)點(diǎn):空間和存儲(chǔ)效率 缺點(diǎn):如果對(duì)象模型的nonstatic data members 有所修改,也就是對(duì)象內(nèi)部布局有所更改,那么使用該對(duì)象的應(yīng)用程序需要重新編譯.

            虛擬繼承:不管基類被派生了多少次,永遠(yuǎn)只有一個(gè)實(shí)體(菱形繼承最下面對(duì)象中只有一個(gè)頂部類實(shí)體)

            繼承的實(shí)現(xiàn):一種可以用basetable 在basetable中指向一個(gè)baseclass的地址,但這會(huì)隨著繼承深度越來(lái)越深變得慢

            現(xiàn)在采用的方法之后記錄~~~ :P

            書中的程序:對(duì)象模型對(duì)程序的影響:

            X foobar(){
                X xx;
                X *px = new X;
                xx.foo();
                px->foo(); //foo是一個(gè)virtual function
                delete px;
                return xx;
            }
            //調(diào)用處 X _result;
            //      foobar(_result);
            void foobar(X& _result){
                _result.X::X();//構(gòu)造result
                px = _new(sizeof(X));
                if(px!=0)
                    px->X::X;
                foo(&_result);//使用result取代xx但是不激活virtual機(jī)制
                (*px->vtbl[2])(px);//使用virtual機(jī)制擴(kuò)展px->foo();
                if(px!=0){
                    (*px->vtbl[1])(px);//使用virtual調(diào)用destructor
                    delete px;
                }
                //不用使用named return statement
                return ;
            }

            ------------------------------------------------------------------------------------

            關(guān)鍵詞帶來(lái)的麻煩~~

            struct 和 class 在C++中可以混用,并不是由于使用哪一個(gè)決定其特性,只是給人的感覺不同罷了class更像是c++中繼承的代名詞 struct更像C中的數(shù)據(jù)集

            如果需要向C函數(shù)傳遞數(shù)據(jù)集合可以使用struct以保證對(duì)象模型的支持

            使用struct完全可以聲明各種class的特性

            對(duì)象的差異

            1.程序模型procedural model  同c一樣

            2.抽象數(shù)據(jù)類型模型 ADT object-base 抽象指的是一族表達(dá)式(public接口)的提供,具體計(jì)算方法未明

            3.面向?qū)ο?oo 有一些彼此相關(guān)的模型通過(guò)一個(gè)抽象的baseclass用以提供公共接口封裝起來(lái)

             

            OO中程序員可能需要處理一個(gè)未知實(shí)體,在執(zhí)行點(diǎn)之前無(wú)法確定,這是由pointers和reference實(shí)現(xiàn)的而在ADT中程序員處理的是一個(gè)擁有固定單一類型的實(shí)體,編譯時(shí)期就已經(jīng)確定了~

            c++中的多態(tài):1.把derived class的指針轉(zhuǎn)化成一個(gè)基類的指針 2virtualfunctions 3.dynamic_cast 和typeid運(yùn)算符

            多態(tài)主要是經(jīng)由一個(gè)共同的接口來(lái)影響類型的封裝,這個(gè)接口通常被放置在抽象的baseclass中,這個(gè)共享接口是以virtual functions機(jī)制來(lái)引發(fā)的,可以在執(zhí)行期間根據(jù)object的真正類型解析出到底是哪一個(gè)函數(shù)實(shí)體被調(diào)用。

            我們的代碼可以避免由于增加某一個(gè)特定的derived 實(shí)體而改變,只需要新的derivedclass重寫這樣的接口便可。

            //測(cè)試指針引用的多態(tài)
            #include <iostream>
                using namespace std;
                struct Point{
                public:
                    Point(float x=0.):_x(x){}
                    float x(){return _x;}
                    void x(float xval){_x=xval;}
                    virtual void  out(){
                        cout<<"I am a Point"<<endl;
                    }
                protected:
                    float _x;
                };
                struct Point2d:public Point{
                public:
                    Point2d(float x=0.,float y=0.):Point(x),_y(y){}
                    float y(){return _y;}
                    void y(float yval){_y=yval;}
                    virtual void out(){
                        cout<<"I am a Point2d"<<endl;
                    }
                protected:
                    float _y;
                };
                int main(){
                    Point pt;
                    Point2d pt2;
                    Point* ptr;
                    Point2d* ptr2;
                    pt.out(); pt2.out();
                    pt=pt2;pt.out();
                    ptr=&pt2;
                    ptr->out();
                    (*ptr).out();
                    Point& ptref=pt2;
                    ptref.out();
                }

            output:

            I am a Point
            I am a Point2d
            I am a Point
            I am a Point2d
            I am a Point2d
            I am a Point2d

            指針和引用可以實(shí)現(xiàn)多態(tài),因?yàn)橹羔樅鸵玫馁x值只是改變其指向的范圍,并沒有觸及對(duì)象模型的改變,因此可以產(chǎn)生多態(tài)的效果。而value的賦值操作會(huì)導(dǎo)致對(duì)象模型的切割,從而真實(shí)的改變了對(duì)象的內(nèi)部模型與類型,失去了多態(tài)的效果。oo 不支持對(duì)對(duì)象的直接處理

             

            指針的類型:

            不同的指針從內(nèi)存角度來(lái)看沒什么不同,只是占據(jù)了一個(gè)word的空間,但是指針的類型會(huì)告訴編譯器如何解釋某個(gè)特定地址中的內(nèi)存內(nèi)容以及大小

             

            OB設(shè)計(jì)也就是ADT設(shè)計(jì)比OO設(shè)置更有效率因?yàn)槠渌泻瘮?shù)引發(fā)操作均在編譯器確定OO需要一些運(yùn)行時(shí)確定,對(duì)象構(gòu)建起來(lái)不需要設(shè)置virtual機(jī)制

            比OO緊湊因?yàn)闊o(wú)需支持virtual機(jī)制從而沒有額外負(fù)擔(dān)

            但是OB相對(duì)來(lái)說(shuō)彈性較小~~

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            posted @ 2010-03-03 13:58 rikisand 閱讀(1934) | 評(píng)論 (0)編輯 收藏

            ----節(jié)錄自清華電機(jī)彭明輝老師于系刊發(fā)表的文章
            許多同學(xué)應(yīng)該都還記得聯(lián)考前夕的焦慮:差一分可能要掉好幾個(gè)志愿,甚至于一生的命運(yùn)從此改觀!到了大四,這種焦慮可能更強(qiáng)烈而復(fù)雜:到底要先當(dāng)兵,就業(yè),還是先考研究所?我就經(jīng)常碰到學(xué)生充滿焦慮的問(wèn)我這些問(wèn)題。可是,這些焦慮實(shí)在是莫須有的!生命是一種長(zhǎng)期而持續(xù)的累積過(guò)程,絕不會(huì)因?yàn)閱我坏氖录鴼Я艘粋€(gè)人的一生,也不會(huì)因?yàn)閱我坏氖录攘艘粋€(gè)人的一生。屬于我們?cè)摰玫模t早會(huì)得到;屬于我們不該得的,即使僥幸巧取也不可能長(zhǎng)久保有。如果我們看清這個(gè)事實(shí),許多所謂"人生的重大抉擇"就可以淡然處之,根本無(wú)需焦慮。而所謂"人生的困境",也往往當(dāng)下就變得無(wú)足掛齒。
              我自己就是一個(gè)活生生的例子。從一進(jìn)大學(xué)就決定不再念研究所,所以,大學(xué)四年的時(shí)間多半在念人文科學(xué)的東西。畢業(yè)后工作了幾年,才決定要念研究所。碩士畢業(yè)后,立下決心:從此不再為文憑而念書。誰(shuí)知道,世事難料,當(dāng)了五年講師后,我又被時(shí)勢(shì)所迫,出國(guó)念博士。
              出國(guó)時(shí),一位大學(xué)同學(xué)笑我:全班最晚念博士的都要回國(guó)了,你現(xiàn)在才要出去??jī)赡旰笪覐膭蚧貋?lái),覺得人生際遇無(wú)常,莫此為甚:一個(gè)從大一就決定再也不鉆營(yíng)學(xué)位的人,竟然連碩士和博士都拿到了!屬于我們?cè)摰玫模臉釉?jīng)少過(guò)?而人生中該得與不該得的究竟有多少,我們又何曾知曉?從此我對(duì)際遇一事不能不更加淡然。
              當(dāng)講師期間,有些態(tài)度較極端的學(xué)生會(huì)當(dāng)面表現(xiàn)出他們的不屑;從劍橋回來(lái)時(shí),卻被學(xué)生當(dāng)做不得了的事看待。這種表面上的大起大落,其實(shí)都是好事者之言,完全看不到事實(shí)的真相。從表面上看來(lái),兩年就拿到劍橋博士,這好像很了不起。但是,在這"兩年"之前我已經(jīng)花整整一年,將研究主題有關(guān)的論文全部看完,并找出研究方向;而之前更已花三年時(shí)間做控制方面的研究,并且在國(guó)際著名的學(xué)術(shù)期刊中發(fā)表論文。而從碩士畢業(yè)到拿博士,期間七年的時(shí)間我從不停止過(guò)研究與自修。所以,這個(gè)博士其實(shí)是累積了七年的成果,或者,只算我花在控制學(xué)門的時(shí)間,也至少有五年),根本也沒什么好驚訝的。
              常人不從長(zhǎng)期而持續(xù)的累積過(guò)程來(lái)看待生命因積蓄而有的成果,老愛在表面上以斷裂而孤立的事件夸大議論,因此每每在平淡無(wú)奇的事件上強(qiáng)做悲喜。可是對(duì)我來(lái)講,當(dāng)講師期間被學(xué)生瞧不起,以及劍橋剛回來(lái)時(shí)被同學(xué)夸大本事,都只是表象。事實(shí)是:我只在乎每天二十四小時(shí)點(diǎn)點(diǎn)滴滴的累積。
              拿碩士或博士只是特定時(shí)刻里這些成果累積的外在展示而已,人生命中真實(shí)的累積從不曾因這些事件而終止或添加。
              常有學(xué)生滿懷憂慮的問(wèn)我:"老師,我很想先當(dāng)完兵,工作一兩年再考研究所。這樣好嗎?"
              "很好,這樣子有機(jī)會(huì)先用實(shí)務(wù)來(lái)印證學(xué)理,你念研究所時(shí)會(huì)比別人了解自己要的是什么。"
              "可是,我怕當(dāng)完兵又工作后,會(huì)失去斗志,因此考不上研究所。"
              "那你就先考研究所好了。"
              "可是,假如我先念研究所,我怕自己又會(huì)像念大學(xué)時(shí)一樣茫然,因此念的不甘不愿的。"
              "那你還是先去工作好了!"
              "可是。。。。。。。
              我完全可以體會(huì)到他們的焦慮,可是卻無(wú)法壓抑住對(duì)于這種話的感慨。其實(shí),說(shuō)穿了他所需要的就是兩年研究所加兩年工作,以便加深知識(shí)的深廣度和獲取實(shí)務(wù)經(jīng)驗(yàn)。
              先工作或先升學(xué),表面上大相逕庭,其實(shí)骨子里的差別根本可以忽略。
              在"朝三暮四"這個(gè)成語(yǔ)故事里,主人原本喂養(yǎng)猴子的橡實(shí)是"早上四顆下午三顆",后來(lái)改為"朝三暮四",猴子就不高興而堅(jiān)持改回到"朝四暮三"。其實(shí),先工作或先升學(xué),期間差異就有如"朝三暮四"與"朝四暮三",原不值得計(jì)較。但是,我們經(jīng)常看不到這種生命過(guò)程中長(zhǎng)遠(yuǎn)而持續(xù)的累積,老愛將一時(shí)際遇中的小差別夸大到生死攸關(guān)的地步。 
              最諷刺的是:當(dāng)我們面對(duì)兩個(gè)可能的方案,而焦慮得不知如何抉擇時(shí),通常表示這兩個(gè)方案可能一樣好,或者一樣壞,因而實(shí)際上選擇哪個(gè)都一樣,唯一的差別只是先后之序而已。而且,愈是讓我們焦慮得厲害的,其實(shí)差別越小,愈不值得焦慮。反而真正有明顯的好壞差別時(shí),我們輕易的就知道該怎么做了。可是我們卻經(jīng)常看不到長(zhǎng)遠(yuǎn)的將來(lái),短視的盯著兩案短期內(nèi)的得失:想選甲案,就舍不得乙案的好處;想選乙案,又舍不得甲案的好處。如果看得夠遠(yuǎn),人生長(zhǎng)則八、九十,短則五、六十年,先做哪一件事又有什么關(guān)系?甚至當(dāng)完兵又工作后,再花一整年準(zhǔn)備研究所,又有什么了不起?當(dāng)然,有些人還是會(huì)憂慮說(shuō):"我當(dāng)完兵又工作后,會(huì)不會(huì)因?yàn)榧依刍蛴洃浟λネ硕容^難考上研究所?"我只能這樣回答:"一個(gè)人考不上研究所,只有兩個(gè)可能:或者他不夠聰明,或者他的確夠聰明。不夠聰明而考不上,那也沒什么好抱怨的。假如你夠聰明,還考不上研究所,那只能說(shuō)你的決心不夠強(qiáng)。假如你是決心不夠強(qiáng),就表示你生命中還有其他的可能性,其重要程度并不下于碩士學(xué)位,而你舍不得丟下他。既然如此,考不上研究所也無(wú)須感到遺憾。不是嗎?"人生的路這么多,為什么要老斤斤計(jì)較著一個(gè)可能性?
              我高中最要好的朋友,一生背運(yùn):高中考兩次,高一念兩次,大學(xué)又考兩次,甚至連機(jī)車駕照都考兩次。畢業(yè)后,他告訴自己:我沒有關(guān)系,也沒有學(xué)歷,只能靠加倍的誠(chéng)懇和努力。現(xiàn)在,他自己擁有一家公司,年收入數(shù)千萬(wàn)。
              一個(gè)人在升學(xué)過(guò)程中不順利,而在事業(yè)上順利,這是常見的事。有才華的人,不會(huì)因?yàn)楸幻>芙^而連帶失去他的才華,只不過(guò)要另外找適合他表現(xiàn)的場(chǎng)所而已。反過(guò)來(lái),一個(gè)人在升學(xué)過(guò)程中太順利,也難免因而放不下身段去創(chuàng)業(yè),而只能乖乖領(lǐng)薪水過(guò)活。福兮禍兮,誰(shuí)人知曉?我們又有什么好得意?又有什么好憂慮?人生的得與失,有時(shí)候怎么也說(shuō)不清楚,有時(shí)候卻再簡(jiǎn)單不過(guò)了:我們得到平日累積的成果,而失去我們不曾努力累積的!所以重要的不是和別人比成就,而是努力去做自己想做的。最后該得到的不會(huì)少你一分,不該得到的也不會(huì)多你一分。
              好像是前年的時(shí)候,我遇到一位高中同學(xué)。他在南加大當(dāng)電機(jī)系的副教授,被清華電機(jī)聘回來(lái)開短期課程。從高中時(shí)代他就很用功,以第一志愿上臺(tái)大電機(jī)后,四年都拿書卷獎(jiǎng),相信他在專業(yè)上的研究也已卓然有成。回想高中入學(xué)時(shí),我們兩個(gè)人的智力測(cè)驗(yàn)成績(jī)分居全學(xué)年第一,第二名。可是從高一我就不曾放棄自己喜歡的文學(xué),音樂(lè),書法,藝術(shù)和哲學(xué),而他卻始終不曾分心,因此兩個(gè)人在學(xué)術(shù)上的差距只會(huì)愈來(lái)愈遠(yuǎn)。反過(guò)來(lái)說(shuō),這十幾二十年我在人文領(lǐng)域所獲得的滿足,恐怕已遠(yuǎn)非他能理解的了。我太太問(wèn)過(guò)我,如果我肯全心專注于一個(gè)研究領(lǐng)域,是不是至少會(huì)趕上這位同學(xué)的成就?我不這樣想,兩個(gè)不同性情的人,注定要走兩條不同的路。不該得的東西,我們注定是得不到的,隨隨便便拿兩個(gè)人來(lái)比,只看到他所得到的,卻看不到他所失去的,這有什么意義?
              有次清華電臺(tái)訪問(wèn)我:"老師你如何面對(duì)你人生中的困境?"我當(dāng)場(chǎng)愣在那里,怎么樣都想不出我這一生什么時(shí)候有過(guò)困境!后來(lái)仔細(xì)回想,才發(fā)現(xiàn):我不是沒有過(guò)困境,而是被常人當(dāng)作"困境"的境遇,我都當(dāng)作一時(shí)的際遇,不曾在意過(guò)而已。剛服完兵役時(shí),長(zhǎng)子已出生卻還找不到工作。我曾焦慮過(guò),卻又覺得遲早會(huì)有工作,報(bào)酬也不至于低的離譜,不曾太放在心上。念碩士期間,家計(jì)全靠太太的薪水,省吃儉用,對(duì)我而言又算不上困境。一來(lái)精神上我過(guò)的很充實(shí),二來(lái)我知道這一切是為了讓自己有機(jī)會(huì)轉(zhuǎn)行去教書(做自己想做的事)。三十一歲才要出國(guó),而同學(xué)正要回系上任教,我很緊張(不知道劍橋要求的有多嚴(yán)),卻不曾喪氣。因?yàn)椋抑雷约哼^(guò)去一直很努力,也有很滿意的心得和成果,只不過(guò)別人看不到而已。  
              我沒有過(guò)困境,因?yàn)槲覐牟辉诤跬庠诘牡檬В膊晃鋽嗟暮蛣e人比高下,而只在乎自己內(nèi)在真實(shí)的累積。  
              我沒有過(guò)困境,因?yàn)槲掖_實(shí)了解到:生命是一種長(zhǎng)期而持續(xù)的累積過(guò)程,絕不會(huì)因?yàn)閱我坏氖录袆×业钠鸱! ?br>  同時(shí)我也相信:屬于我們?cè)摰玫模t早會(huì)得到;屬于我們不該得的,即使一分也不可能增加。假如你可以持有相同的信念,那么人生于你也會(huì)是寬廣而長(zhǎng)遠(yuǎn),沒有什么了不得的"困境",也沒有什么好焦慮的了。
            注:清華=臺(tái)灣清華.研究所=研究生


            posted @ 2010-03-01 19:36 rikisand 閱讀(105) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題
            共5頁(yè): 1 2 3 4 5 
            人妻无码αv中文字幕久久琪琪布 人妻无码精品久久亚瑟影视 | 日本精品久久久中文字幕| 精品熟女少妇av免费久久| 亚洲天堂久久精品| 中文国产成人精品久久亚洲精品AⅤ无码精品| 亚洲日韩欧美一区久久久久我| 精品永久久福利一区二区| 久久久WWW免费人成精品| 久久夜色精品国产噜噜麻豆| 93精91精品国产综合久久香蕉 | 久久精品中文字幕一区| 亚洲va久久久噜噜噜久久狠狠| 久久久久久免费一区二区三区| 亚洲欧美精品一区久久中文字幕| 国内精品久久久久久久97牛牛| 亚洲另类欧美综合久久图片区| 久久99国产综合精品免费| 思思久久99热免费精品6| 国产一久久香蕉国产线看观看| 综合久久一区二区三区 | 久久精品国产一区二区电影| 亚洲∧v久久久无码精品| 亚洲欧洲精品成人久久曰影片 | 久久亚洲国产成人精品无码区| 一本久久知道综合久久| 无码任你躁久久久久久老妇| 青青草原综合久久| 国产精品久久久亚洲| 无码AV波多野结衣久久| 一本久道久久综合狠狠爱| 久久久久精品国产亚洲AV无码| 欧美伊人久久大香线蕉综合69| 青青草国产精品久久久久| 欧美久久精品一级c片片| 久久97精品久久久久久久不卡| 亚洲成色WWW久久网站| 亚洲精品国产综合久久一线| 中文字幕精品无码久久久久久3D日动漫 | 亚洲а∨天堂久久精品9966| 久久久精品人妻无码专区不卡| 久久久国产精华液|