• <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.為啥要重載操作符:

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

            2.哪些不能重載

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

            3.需要注意的地方:

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

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

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

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

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

            ++ -- 一般設(shè)置為成員函數(shù) ,因為可能改變對象狀態(tài)

            4.定義輸入輸出操作符

            io操作符只能重載為非成員函數(shù),否則做操作符只能是對象成員 用法變成了 object<<cin  不符合我們的習(xí)慣,經(jīng)常把他們設(shè)置成為友元,因為可能觸及私有變量。

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

            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)用默認構(gòu)造函數(shù)恢復(fù)對象狀態(tài)。

            5.算術(shù)運算符和關(guā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ù)運算符設(shè)置為非成員函數(shù),與內(nèi)置運算符對應(yīng),選擇返回value 而不是引用。賦值運算符重載為成員函數(shù),并用它來實現(xiàn)算術(shù)運算符,這樣算術(shù)運算符不用friend

            相等運算符和不等運算符一般成對出現(xiàn),且用一個實現(xiàn)另一個

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

            6.賦值操作符

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

            =和+=  -= 一般都需要返回左操作數(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.成員訪問操作符

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

            例子:auto-ptr~~~~

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

                 // 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
                 };

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

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

            可以這樣使用 ScreenPtr 對象訪問 Screen 對象的成員:

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

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

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

            6.自增自減操作符

            一般重載為成員函數(shù),為了與內(nèi)置類型一致,前置操作符返回運算結(jié)果引用,后置操作符返回運算前的值,value not ref ,為了區(qū)分,后置操作符提供了一個實參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 用前置實現(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ù)對象

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

             

            通過為類類型的對象提供一個實參表而使用調(diào)用操作符,所用的方式看起來像一個函數(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ù)。一個類可以定義函數(shù)調(diào)用操作符的多個版本,由形參的數(shù)目或類型加以區(qū)別。

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

            函數(shù):

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

            函數(shù)對象:

            // 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ù)對象的便捷性】

                     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器,它通過將一個操作數(shù)綁定到給定值而將二元函數(shù)對象轉(zhuǎn)換為一元函數(shù)對象

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

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

            轉(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ù)不能指定返回類型,但是每個轉(zhuǎn)換函數(shù)必須顯式返回一個指定類型的值。例如,operator int 返回一個 int 值;如果定義 operator Sales_item,它將返回一個 Sales_item 對象,諸如此類。

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

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

          1. In expressions:

            在表達式中:

                 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ù)返回值:

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

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

             

             

             

             

             

             

             

             

             

             

             

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

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

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

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

               首先,可重入和線程安全是兩個并不等同的概念,一個函數(shù)可以是可重入的,也可以是線程安全的,可以兩者均滿足,可以兩者皆不滿組(該描述嚴(yán)格的說存在漏洞,參見第二條)。
                其次,從集合和邏輯的角度看,可重入是線程安全的子集,可重入是線程安全的充分非必要條件。可重入的函數(shù)一定是線程安全的,然過來則不成立。
                第三,POSIX 中對可重入和線程安全這兩個概念的定義:
            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.
               另外還有一個 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)用的問題上,可重入函數(shù)在signal處理函數(shù)中可以被安全調(diào)用,因此同時也是Async-Signal-Safe Function;而線程安全函數(shù)不保證可以在signal處理函數(shù)中被安全調(diào)用,如果通過設(shè)置信號阻塞集合等方法保證一個非可重入函數(shù)不被信號中斷,那么它也是Async-Signal-Safe Function。
                 值得一提的是POSIX 1003.1的System Interface缺省是Thread-Safe的,但不是Async-Signal-Safe的。Async-Signal-Safe的需要明確表示,比如fork ()和signal()。
            最后讓我們來構(gòu)想一個線程安全但不可重入的函數(shù):
               假設(shè)函數(shù)func()在執(zhí)行過程中需要訪問某個共享資源,因此為了實現(xiàn)線程安全,在使用該資源前加鎖,在不需要資源解鎖。
               假設(shè)該函數(shù)在某次執(zhí)行過程中,在已經(jīng)獲得資源鎖之后,有異步信號發(fā)生,程序的執(zhí)行流轉(zhuǎn)交給對應(yīng)的信號處理函數(shù);再假設(shè)在該信號處理函數(shù)中也需要調(diào)用函數(shù)func(),那么func()在這次執(zhí)行中仍會在訪問共享資源前試圖獲得資源鎖,然而我們知道前一個func()實例已然獲得該鎖,因此信號處理函數(shù)阻塞——另一方面,信號處理函數(shù)結(jié)束前被信號中斷的線程是無法恢復(fù)執(zhí)行的,當(dāng)然也沒有釋放資源的機會,這樣就出現(xiàn)了線程和信號處理函數(shù)之間的死鎖局面。
                因此,func()盡管通過加鎖的方式能保證線程安全,但是由于函數(shù)體對共享資源的訪問,因此是非可重入。

            posted @ 2010-03-12 14:33 rikisand 閱讀(250) | 評論 (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次 然后 線性篩法限定了 任何一個合數(shù)只被它的最小質(zhì)因數(shù)篩掉一次,怎么做到這一點~~

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

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

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

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

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

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

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

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

            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 的時候只有調(diào)用了A的析構(gòu)函數(shù),真正的B并沒有調(diào)用。而如果虛擬的話,那么a調(diào)用析構(gòu)函數(shù)會調(diào)用B的析構(gòu)函數(shù),而B的析構(gòu)函數(shù)會調(diào)用A的析構(gòu)函數(shù)。

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

            ---------

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

            Plain OI’s data 形式

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

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

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

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

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

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

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

            例如 void mumble(){

            Point A1={1};

            Point A2; A2._x=1;

            }

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

            explicit initialzation list (A1的那種)缺點: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ù)值,因此默認語義下情況良好。virtual destructor 也沒有引入啊,同樣道理,行為良好即可

            virtual func 的引入給我們的 Point class 帶來了膨脹作用:

            定義的構(gòu)造函數(shù)被附加了一些代碼,用來初始化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;

            }

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

            因為bitwise的賦值可能會帶給vptr 錯誤

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

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

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

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

            2.如果一個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 不再列表,有默認ctor 調(diào)用

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

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

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

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

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

            虛擬繼承

            虛擬繼承比較具有特殊性,因為他具有共享性。在這樣一個繼承體系中

            image 

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

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

             

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

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

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

            另一種方法是控制vptr,

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

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

            一個 PVertex對象會先形成一個 Point對象- >Point3d->Vertex->Vertex3d->PVertex

            ctor執(zhí)行算法:

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

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

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

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

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

            1.對象完整定義后

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

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

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

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

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

            我們有三種選擇:

            1 什么都不做,按默認行為實施

            2 提供一個explicit copy assignment operator

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

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

            1.class 有一個member object 而他有copy operator

            2.class的base class 有一個copy operator

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

            4.class 繼承自 virtual base class

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

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

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

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

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

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

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

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

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

             

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

            對象的蛻變會因為vptr 重新設(shè)定受到影響(dtor中,程序員代碼之前),在程序中施行dtor的真正語義會在下一章具體表述~~

            #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 閱讀(1841) | 評論 (0)編輯 收藏

            Function 語義學(xué)

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

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

            float Point3d::mangitude3d()const{…}

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

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

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

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

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

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

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

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

            類似的magnitude 會變成

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

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

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

            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)用的

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

             

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

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

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

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

            ~~~單一繼承

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

             

            當(dāng)一個class 繼承自上一個class時候

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

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

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

            ~~~多重繼承呢

            多重繼承時候 例如 Derived public 自 Base1,Base2

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

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

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

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

            Bjarne采用擴充vtable 每一項記錄調(diào)整this指針的信息,但浪費,因為大部分不需要調(diào)整

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

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

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

            可以看到”:

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

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

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

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

            ~~~虛擬繼承下的virtual func

              即便只有一個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是一個虛擬函數(shù)

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

            答案~~是的

            因為取得虛擬函數(shù)的地址其實取得的是虛擬函數(shù)的offset值

            調(diào)用會變成  (*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種解決方法:

            一種:單一繼承的情況(帶vcall thunk地址或者函數(shù)地址)

            2多重繼承 帶有faddr 和delta

            虛擬繼承 帶有四個members

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

            ----------

            inline members

            真正的inline 函數(shù)擴展是在調(diào)用的那一個點上,這回帶來參數(shù)的求值操作以及暫時性對象的管理

             

            形式參數(shù) formal arguments

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

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

            例如:

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

             

            這會擴展成: 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);}

            為了維護局部變量可能會變成:

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

            一般而言,inline 函數(shù)的每一個局部變量都必須放在函數(shù)調(diào)用的一個封閉區(qū)段中,擁有一個獨一無二的名字,如果inline函數(shù)以單一表達式擴展多次,那么每次擴展都需要自己的一組局部變量。如果inline 函數(shù)可以以分離的多個式子被擴展多次,那么只需要一組局部變量就可以重復(fù)使用,因為他們被封閉在自己的scope中:

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

            擴展 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ù)帶有副作用或者是以一個單一表達式做多重調(diào)用,或者是在inline 函數(shù)內(nèi)部有多個局部變量

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

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

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

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

            DATA 語義學(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其實是私有變量,B不應(yī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.語言本身造成的負擔(dān):如virtual baseclass

            2.對齊造成的負擔(dān)(對齊會單獨開題討論)

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

            看上面的代碼”:

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

            A的大小為1 因為有一個char 沒有對齊因為不是結(jié)構(gòu)型對象,如果A沒有這個char大小依然是1的

            B的大小為9 首先開始是它的virtual base class ptr,4個字節(jié)的指針,然后是他自己的member 1個char 此時為了保證其對象完整性編譯器對齊到4字節(jié)處也就是在第九個字節(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é)對齊)然后是C的4字節(jié)vbptr,然后是自己的member4字節(jié)最后是1字節(jié)的base class member,可以看到B和C的base class table中的項都是自己位置與這個member的offset 值

             

            不同編譯器可能結(jié)果不同的,因為c++ standard 并沒有強制規(guī)定 base class subobjects的順序等

             

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

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

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

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

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

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

             

            -----DATA member 的綁定

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

            ---- DATA 的存取

            Point3d origin,*pt=&origin;

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

            如果x是靜態(tài)data member 則完全沒有區(qū)別 因為他們都在data segment 中和object無關(guān)

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

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

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

            多繼承或者單繼承都不會帶來訪問上的影響,因為他們都可以向c的結(jié)構(gòu)體那樣在編譯時期確定各個member的offset。即使是多繼承pt指向第二個baseclass的data,由于member的位置在編譯時期就已經(jīng)固定了,因此存取member只是一個offset運算,像單一繼承那樣簡單,不管是指針,reference或者object都一樣

            只有virtual base class 會帶來一些損耗,因為他使得對象模型變得復(fù)雜了

            如果我們在一個類中加入了virtual func 會發(fā)生什么~~~

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

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

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

            4.加強dtor 消除vptr 從dirived class 到 base class

             

            虛擬繼承:

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

            那么一個object會分成一個不變局部和一個共享局部的數(shù)據(jù),共享局部就是virtual base class subobject,他的位置會因為每次派生操作發(fā)生變化(例如一個virtual base class subobject的位置在不同級別的繼承體系中的位置是不確定的,不像多繼承單繼承那樣有固定的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

            因為為了防止&A::a和 int A::*a = 0;一樣把他加了1。

             

            虛擬繼承帶來的主要沖擊是,妨礙了優(yōu)化的有效性,因為每一層虛擬繼承都帶來了一個額外層次的間接性,在編譯器中存取 類似point::x的操作pb.*bx

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

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

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

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

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

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

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

            Default Ctor 在需要的時候被構(gòu)建出來~

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

            例如:

                  class Foo(public:int val;);

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

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

            1.帶有Default Ctor 的member class object

            很好理解,既然內(nèi)部成員含有default ctor 那么我們創(chuàng)建新的對象時需要調(diào)用它,而我們并木有調(diào)用它的函數(shù),編譯器自然會幫我們提供一個。如果我們提供了default ctor ,那么編譯器會在我們提供的函數(shù)加入調(diào)用必須調(diào)用的member class 的default ctor。同樣的在每一個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 那么編譯器會創(chuàng)建一個,并在其中依照聲明順序依次調(diào)用dopey sneezy bashful的default ctor 然而如果:

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

            編譯器會擴張為

            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)用,編譯器會為想上面一樣為我們加上

            3.含有virtual functions的Class

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

            4.帶有virtual base class

            virtual base 實現(xiàn)方法各有不同,然而共同點是必須是virtual base class 在其每一個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在對象中的位置并不是固定的,而是在運行時真正確定pa指向什么對象A還是C才能確定的,因此需要設(shè)定一個指向基類subobject的指針,所以需要ctor工作了

            OK:

            1.任何class 如果沒有定義Default ctor 就會被合成一個

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

            錯的很easy了~

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

            再來看 Copy Ctor:

            copy ctor 負責(zé)用另一個對象初始化一個對象

            operator = 負責(zé)用另一個對象給一個對象賦值

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

            Default member initialization~~~

            也就是memberwise 的initialization

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

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

            什么時候不可以呢~

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

            2  繼承一個base class 后者有copy ctor

            3  含有virtual func

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

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

            3 中:

            如果兩個同樣類型的object賦值時,沒有問題因為他們的vptr相同

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

            4中:

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

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

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

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

            X x0;

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

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

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

             

            參數(shù)的初始化:

            一種策略導(dǎo)入暫時的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ù)直接建構(gòu)造應(yīng)該在的位置上,函數(shù)返回時局部對象的destructor 會執(zhí)行

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

             

            返回值的初始化:

            X bar(){

                X xx;

                return xx;

            }

            返回值如何從局部對象xx拷貝而來呢?

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

            2.return 指令之前安插一個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;

                通過 y z 計算xx

               return xx;

            }

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

            如果定義 ctor來利用yz計算xx則:

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

                 return X(y,z);

            }

            變成:

            bar(X& result){

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

                 return;

            }

            無需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)化了}

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

             

            Copy ctor 要不要:

            如果一個class 符合bitwise的要求那么此時member wise 的拷貝已經(jīng)高效簡潔 無需加入了

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

             

            成員們的初始化過程:

            什么時候必須用初始化列表:

            1.初始化一個reference時 2.初始化一個const member 3.調(diào)用父類ctor而且有參數(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放到初始化列表會更有效率 ,會變成

            name.String::String(0);

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

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

            編譯器會一個個操作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)}

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

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

            由于會安插在explicit code 之前 所以沒問題 會變成 j=val;   i=j;

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

            答案是可以的,因為和objects相關(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ù)制方式進行的,此時編譯器并沒有直接建構(gòu)結(jié)果于rvo上 ,看看下面的試驗:

            注:明白了, 結(jié)果直接建構(gòu)在hiddern,然后通過operator = 傳給rvo 。沒有copy ctor因為拷貝構(gòu)造函數(shù)是負責(zé)初始化的,而operator = 才是用來賦值的.

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

            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);  //此時定義和賦值放到了一個表達式子
                return 0;
            }

            output:

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

            ~~~~3 cl /o2  再來~~~~ NRV出馬

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

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

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

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

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

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

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

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

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

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

            封裝在空間上的代價:

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

            c++在空間和存取效率的代價主要由virtual帶來:

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

            virtual class 機制:

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

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

            c++對象模型:也就是我們怎么在內(nèi)存中安排各個數(shù)據(jù)成員以及成員函數(shù)

            1.簡單對象模型~ 對象由各個slot組成,每一個slot包含指針,指向各個成員

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

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

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

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

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

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

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

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

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

            書中的程序:對象模型對程序的影響:

            X foobar(){
                X xx;
                X *px = new X;
                xx.foo();
                px->foo(); //foo是一個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機制
                (*px->vtbl[2])(px);//使用virtual機制擴展px->foo();
                if(px!=0){
                    (*px->vtbl[1])(px);//使用virtual調(diào)用destructor
                    delete px;
                }
                //不用使用named return statement
                return ;
            }

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

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

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

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

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

            對象的差異

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

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

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

             

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

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

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

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

            //測試指針引用的多態(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

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

             

            指針的類型:

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

             

            OB設(shè)計也就是ADT設(shè)計比OO設(shè)置更有效率因為其所有函數(shù)引發(fā)操作均在編譯器確定OO需要一些運行時確定,對象構(gòu)建起來不需要設(shè)置virtual機制

            比OO緊湊因為無需支持virtual機制從而沒有額外負擔(dān)

            但是OB相對來說彈性較小~~

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

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

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


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

            僅列出標(biāo)題
            共5頁: 1 2 3 4 5 
            亚洲国产精品一区二区久久hs| 男女久久久国产一区二区三区| 思思久久99热只有频精品66| 国产成人久久激情91| 大香伊人久久精品一区二区| 久久96国产精品久久久| 精品综合久久久久久97| 狠狠久久综合伊人不卡| 久久精品99久久香蕉国产色戒 | 麻豆av久久av盛宴av| www亚洲欲色成人久久精品| 久久综合九色综合网站| 免费久久人人爽人人爽av| 久久久久18| 久久综合九色综合久99| 午夜精品久久久久久久久| 欧美日韩精品久久久久| 99久久精品免费看国产| 国产精品99久久久久久人| 国内精品久久久久影院薰衣草| 久久久久久毛片免费看| 国产精品va久久久久久久| 久久精品国产半推半就| 久久久久女人精品毛片| 国产成人无码精品久久久性色 | 精品一久久香蕉国产线看播放| 久久99精品国产麻豆| 人妻少妇久久中文字幕| 亚洲狠狠婷婷综合久久久久| 欧美精品国产综合久久| 久久午夜夜伦鲁鲁片免费无码影视 | 国产精品久久久久影院色| 久久水蜜桃亚洲av无码精品麻豆| 亚洲综合伊人久久大杳蕉| 狠狠精品久久久无码中文字幕 | 亚洲精品国产自在久久| 久久嫩草影院免费看夜色| 久久伊人色| 久久亚洲AV无码精品色午夜| 伊人久久久AV老熟妇色| 亚洲综合伊人久久综合|