• <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>

            寶杉的博客

            UNIX/LINUX;ACE;SNMP;C++
            posts - 33, comments - 23, trackbacks - 0, articles - 0

             

            1、什么是const?
               常類型是指使用類型修飾符const說明的類型,常類型的變量或?qū)ο蟮闹凳遣荒鼙桓碌摹#ó?dāng)然,我們可以偷梁換柱進(jìn)行更新:)

            2、為什么引入const?
              const 推出的初始目的,正是為了取代預(yù)編譯指令,消除它的缺點(diǎn),同時(shí)繼承它的優(yōu)點(diǎn)。

            3、cons有什么主要的作用?
               (1)可以定義const常量,具有不可變性。
                    例如:
                         const int Max=100;
                         int Array[Max];       
               (2)便于進(jìn)行類型檢查,使編譯器對處理內(nèi)容有更多了解,消除了一些隱患。
             例如:
                         void f(const int i) { .........}
                    編譯器就會(huì)知道i是一個(gè)常量,不允許修改;
               (3)可以避免意義模糊的數(shù)字出現(xiàn),同樣可以很方便地進(jìn)行參數(shù)的調(diào)整和修改。
                    同宏定義一樣,可以做到不變則已,一變都變!如(1)中,如果想修改Max的內(nèi)容,只需要:const int Max=you want;即可!
               (4)可以保護(hù)被修飾的東西,防止意外的修改,增強(qiáng)程序的健壯性。
                    還是上面的例子,如果在函數(shù)體內(nèi)修改了i,編譯器就會(huì)報(bào)錯(cuò);
                    例如:
                         void f(const int i) { i=10;//error! }
                (5) 為函數(shù)重載提供了一個(gè)參考。
                     class A
                     {
                       ......
                       void f(int i)       {......} file://一個(gè)函數(shù)
                       void f(int i) const {......} file://上一個(gè)函數(shù)的重載
                        ......
                      };
                 (6) 可以節(jié)省空間,避免不必要的內(nèi)存分配。
                     例如:
                          #define PI 3.14159         file://常量宏
                          const doulbe  Pi=3.14159;  file://此時(shí)并未將Pi放入ROM中
                          ......
                          double i=Pi;               file://此時(shí)為Pi分配內(nèi)存,以后不再分配!
                          double I=PI;               file://編譯期間進(jìn)行宏替換,分配內(nèi)存
                          double j=Pi;               file://沒有內(nèi)存分配
                          double J=PI;               file://再進(jìn)行宏替換,又一次分配內(nèi)存!
                     const定義常量從匯編的角度來看,只是給出了對應(yīng)的內(nèi)存地址,而不是象#define一樣給出的是立即數(shù),所以,const定義的常量在程序運(yùn)行過程中只有一份拷貝,而#define定義的常量在內(nèi)存中有若干個(gè)拷貝。
                 (7) 提高了效率。
                       編譯器通常不為普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成為一個(gè)編譯期間的常量,沒有了存儲與讀內(nèi)存的操作,使得它的效率也很高。

            3、如何使用const?
               (1)修飾一般常量
                 一般常量是指簡單類型的常量。這種常量在定義時(shí),修飾符const可以用在類型說明符前,也可以用在類型說明符后。
                   例如:  
                       int const x=2;  或  const int x=2;
               (2)修飾常數(shù)組
                   定義或說明一個(gè)常數(shù)組可采用如下格式:
                   int const a[5]={1, 2, 3, 4, 5}; 
                     const int a[5]={1, 2, 3, 4, 5};
               (3)修飾常對象
                  常對象是指對象常量,定義格式如下:
              class A;
                   const A a;
                     A const a;
                  定義常對象時(shí),同樣要進(jìn)行初始化,并且該對象不能再被更新,修飾符const可以放在類名后面,也可以放在類名前面。 
               (4)修飾常指針
                    const int *A;        file://const修飾指向的對象,A可變,A指向的對象不可變
                    int const *A;       file://const修飾指向的對象,A可變,A指向的對象不可變
                    int *const A;       file://const修飾指針A,     A不可變,A指向的對象可變
                    const int *const A;  file://指針A和A指向的對象都不可變
               (5)修飾常引用
                    使用const修飾符也可以說明引用,被說明的引用為常引用,該引用所引用的對象不能被更新。其定義格式如下:
                   const double & v;
              (6)修飾函數(shù)的常參數(shù)
                    const修飾符也可以修飾函數(shù)的傳遞參數(shù),格式如下:
                    void Fun(const int Var);
                    告訴編譯器Var在函數(shù)體中的無法改變,從而防止了使用者的一些無意的或錯(cuò)誤的修改。    
               (7)修飾函數(shù)的返回值:
                    const修飾符也可以修飾函數(shù)的返回值,是返回值不可被改變,格式如下:
                        const int Fun1();
                        const MyClass Fun2();
               (8)修飾類的成員函數(shù):
                    const修飾符也可以修飾類的成員函數(shù),格式如下:
                        class ClassName
                 {
                         public:
                              int Fun() const;
                                .....
                         };
                    這樣,在調(diào)用函數(shù)Fun時(shí)就不能修改類里面的數(shù)據(jù)
                (9)在另一連接文件中引用const常量
                     extern const int i;     file://正確的引用
                     extern const int j=10;  file://錯(cuò)誤!常量不可以被再次賦值
                另外,還要注意,常量必須初始化!
                     例如:
                         const int i=5; 

            4、幾點(diǎn)值得討論的地方:
               (1)const究竟意味著什么?
                    說了這么多,你認(rèn)為const意味著什么?一種修飾符?接口抽象?一種新類型?
                    也許都是,在Stroustup最初引入這個(gè)關(guān)鍵字時(shí),只是為對象放入ROM做出了一種可能,對于const對象,C++既允許對其進(jìn)行靜態(tài)初始化,也允許對他進(jìn)行動(dòng)態(tài)初始化。理想的const對象應(yīng)該在其構(gòu)造函數(shù)完成之前都是可寫的,在析夠函數(shù)執(zhí)行開始后也都是可寫的,換句話說,const對象具有從構(gòu)造函數(shù)完成到析夠函數(shù)執(zhí)行之前的不變性,如果違反了這條規(guī)則,結(jié)果都是未定義的!雖然我們把const放入ROM中,但這并不能夠保證const的任何形式的墮落,我們后面會(huì)給出具體的辦法。無論const對象被放入ROM中,還是通過存儲保護(hù)機(jī)制加以保護(hù),都只能保證,對于用戶而言這個(gè)對象沒有改變。換句話說,廢料收集器(我們以后會(huì)詳細(xì)討論,這就一筆帶過)或數(shù)據(jù)庫系統(tǒng)對一個(gè)const的修改怎沒有任何問題。
               (2)位元const V.S. 抽象const?
                    對于關(guān)鍵字const的解釋有好幾種方式,最常見的就是位元const 和 抽象const。下面我們看一個(gè)例子:
                    class A
                    {
                     public:
                           ......
                           A f(const A& a);
                           ......
                     };
                     如果采用抽象const進(jìn)行解釋,那就是f函數(shù)不會(huì)去改變所引用對象的抽象值,如果采用位元const進(jìn)行解釋,那就成了f函數(shù)不會(huì)去改變所引用對象的任何位元。
                     我們可以看到位元解釋正是c++對const問題的定義,const成員函數(shù)不被允許修改它所在對象的任何一個(gè)數(shù)據(jù)成員。
                     為什么這樣呢?因?yàn)槭褂梦辉猚onst有2個(gè)好處:
                     最大的好處是可以很容易地檢測到違反位元const規(guī)定的事件:編譯器只用去尋找有沒有對數(shù)據(jù)成員的賦值就可以了。另外,如果我們采用了位元const,那么,對于一些比較簡單的const對象,我們就可以把它安全的放入ROM中,對于一些程序而言,這無疑是一個(gè)很重要的優(yōu)化方式。(關(guān)于優(yōu)化處理,我們到時(shí)候?qū)iT進(jìn)行討論)
                     當(dāng)然,位元const也有缺點(diǎn),要不然,抽象const也就沒有產(chǎn)生的必要了。
                     首先,位元const的抽象性比抽象const的級別更低!實(shí)際上,大家都知道,一個(gè)庫接口的抽象性級別越低,使用這個(gè)庫就越困難。
                     其次,使用位元const的庫接口會(huì)暴露庫的一些實(shí)現(xiàn)細(xì)節(jié),而這往往會(huì)帶來一些負(fù)面效應(yīng)。所以,在庫接口和程序?qū)崿F(xiàn)細(xì)節(jié)上,我們都應(yīng)該采用抽象const。
                     有時(shí),我們可能希望對const做出一些其它的解釋,那么,就要注意了,目前,大多數(shù)對const的解釋都是類型不安全的,這里我們就不舉例子了,你可以自己考慮一下,總之,我們盡量避免對const的重新解釋。
               (3)放在類內(nèi)部的常量有什么限制?
                    看看下面這個(gè)例子:
                    class A
                    {
                     private:
                       const int c3 = 7;           // ???
                   static int c4 = 7;          // ???
                   static const float c5 = 7;  // ???
                      ......
              };
                     你認(rèn)為上面的3句對嗎?呵呵,都不對!使用這種類內(nèi)部的初始化語法的時(shí)候,常量必須是被一個(gè)常量表達(dá)式初始化的整型或枚舉類型,而且必須是static和const形式。這顯然是一個(gè)很嚴(yán)重的限制!
                     那么,我們的標(biāo)準(zhǔn)委員會(huì)為什么做這樣的規(guī)定呢?一般來說,類在一個(gè)頭文件中被聲明,而頭文件被包含到許多互相調(diào)用的單元去。但是,為了避免復(fù)雜的編譯器規(guī)則,C++要求每一個(gè)對象只有一個(gè)單獨(dú)的定義。如果C++允許在類內(nèi)部定義一個(gè)和對象一樣占據(jù)內(nèi)存的實(shí)體的話,這種規(guī)則就被破壞了。
                (4)如何初始化類內(nèi)部的常量?
                     一種方法就是static 和 const 并用,在內(nèi)部初始化,如上面的例子;
                     另一個(gè)很常見的方法就是初始化列表:
                     class A
                     {
                      public:
                            A(int i=0):test(i) {}
                      private:
                            const int i;
                      };
                      還有一種方式就是在外部初始化,例如:
                     class A
                     {
                      public:
                            A() {}
                      private:
                            static const int i;  file://注意必須是靜態(tài)的!
                      };
                      const int A::i=3;
                 (5)常量與數(shù)組的組合有什么特殊嗎?
                      我們給出下面的代碼:
                       const int size[3]={10,20,50};
                       int array[size[2]];
                       有什么問題嗎?對了,編譯通不過!為什么呢?
                       const可以用于集合,但編譯器不能把一個(gè)集合存放在它的符號表里,所以必須分配內(nèi)存。在這種情況下,const意味著“不能改變的一塊存儲”。然而,其值在編譯時(shí)不能被使用,因?yàn)榫幾g器在編譯時(shí)不需要知道存儲的內(nèi)容。自然,作為數(shù)組的大小就不行了:)
                     你再看看下面的例子:
                      class A
                     {
                      public:
                            A(int i=0):test[2]({1,2}) {} file://你認(rèn)為行嗎?
                      private:
                            const int test[2];
                      };
                     vc6下編譯通不過,為什么呢?
                     關(guān)于這個(gè)問題,前些時(shí)間,njboy問我是怎么回事?我反問他:“你認(rèn)為呢?”他想了想,給出了一下解釋,大家可以看看:我們知道編譯器堆初始化列表的操作是在構(gòu)造函數(shù)之內(nèi),顯式調(diào)用可用代碼之前,初始化的次序依據(jù)數(shù)據(jù)聲明的次序。初始化時(shí)機(jī)應(yīng)該沒有什么問題,那么就只有是編譯器對數(shù)組做了什么手腳!其實(shí)做什么手腳,我也不知道,我只好對他進(jìn)行猜測:編譯器搜索到test發(fā)現(xiàn)是一個(gè)非靜態(tài)的數(shù)組,于是,為他分配內(nèi)存空間,這里需要注意了,它應(yīng)該是一下分配完,并非先分配test[0],然后利用初始化列表初始化,再分配test[1],這就導(dǎo)致數(shù)組的初始化實(shí)際上是賦值!然而,常量不允許賦值,所以無法通過。
                    呵呵,看了這一段冠冕堂皇的話,真讓我笑死了!njboy別怪我揭你短呀:)我對此的解釋是這樣的:C++標(biāo)準(zhǔn)有一個(gè)規(guī)定,不允許無序?qū)ο笤陬悆?nèi)部初始化,數(shù)組顯然是一個(gè)無序的,所以這樣的初始化是錯(cuò)誤的!對于他,只能在類的外部進(jìn)行初始化,如果想讓它通過,只需要聲明為靜態(tài)的,然后初始化。
                     這里我們看到,常量與數(shù)組的組合沒有什么特殊!一切都是數(shù)組惹的禍!
               (6)this指針是不是const類型的?
                    this指針是一個(gè)很重要的概念,那該如何理解她呢?也許這個(gè)話題太大了,那我們縮小一些:this指針是個(gè)什么類型的?這要看具體情況:如果在非const成員函數(shù)中,this指針只是一個(gè)類類型的;如果在const成員函數(shù)中,this指針是一個(gè)const類類型的;如果在volatile成員函數(shù)中,this指針就是一個(gè)volatile類類型的。
               (7)const到底是不是一個(gè)重載的參考對象?
                    先看一下下面的例子:
                    class A
                     {
                       ......
                       void f(int i)       {......} file://一個(gè)函數(shù)
                       void f(int i) const {......} file://上一個(gè)函數(shù)的重載
                        ......
                      };
                    上面是重載是沒有問題的了,那么下面的呢?
                     class A
                     {
                       ......
                       void f(int i)       {......} file://一個(gè)函數(shù)
                       void f(const int i) {......} file://?????
                        ......
                     };
                     這個(gè)是錯(cuò)誤的,編譯通不過。那么是不是說明內(nèi)部參數(shù)的const不予重載呢?再看下面的例子:
                    class A
                     {
                       ......
                       void f(int& )       {......} file://一個(gè)函數(shù)
                       void f(const int& ) {......} file://?????
                        ......
                     };
                     這個(gè)程序是正確的,看來上面的結(jié)論是錯(cuò)誤的。為什么會(huì)這樣呢?這要涉及到接口的透明度問題。按值傳遞時(shí),對用戶而言,這是透明的,用戶不知道函數(shù)對形參做了什么手腳,在這種情況下進(jìn)行重載是沒有意義的,所以規(guī)定不能重載!當(dāng)指針或引用被引入時(shí),用戶就會(huì)對函數(shù)的操作有了一定的了解,不再是透明的了,這時(shí)重載是有意義的,所以規(guī)定可以重載。
               (8)什么情況下為const分配內(nèi)存?
                    以下是我想到的可能情況,當(dāng)然,有的編譯器進(jìn)行了優(yōu)化,可能不分配內(nèi)存。
                    A、作為非靜態(tài)的類成員時(shí);
                    B、用于集合時(shí);
                    C、被取地址時(shí);
                    D、在main函數(shù)體內(nèi)部通過函數(shù)來獲得值時(shí);
                    E、const的 class或struct有用戶定義的構(gòu)造函數(shù)、析構(gòu)函數(shù)或基類時(shí);。
                    F、當(dāng)const的長度比計(jì)算機(jī)字長還長時(shí);
                    G、參數(shù)中的const;
                    H、使用了extern時(shí)。
                    不知道還有沒有其他情況,歡迎高手指點(diǎn):)       
               (9)臨時(shí)變量到底是不是常量?
                    很多情況下,編譯器必須建立臨時(shí)對象。像其他任何對象一樣,它們需要存儲空間而且必須被構(gòu)造和刪除。區(qū)別是我們從來看不到編譯器負(fù)責(zé)決定它們的去留以及它們存在的細(xì)節(jié)。對于C++標(biāo)準(zhǔn)草案而言:臨時(shí)對象自動(dòng)地成為常量。因?yàn)槲覀兺ǔ=佑|不到臨時(shí)對象,不能使用與之相關(guān)的信息,所以告訴臨時(shí)對象做一些改變有可能會(huì)出錯(cuò)。當(dāng)然,這與編譯器有關(guān),例如:vc6、vc7都對此作了擴(kuò)展,所以,用臨時(shí)對象做左值,編譯器并沒有報(bào)錯(cuò)。
               (10)與static搭配會(huì)不會(huì)有問題?
                    假設(shè)有一個(gè)類:
                    class A
                    {
                     public:
                         ......
                         static void f() const { ......}
                         ......
                     };
                     我們發(fā)現(xiàn)編譯器會(huì)報(bào)錯(cuò),因?yàn)樵谶@種情況下static不能夠與const共存!
                     為什么呢?因?yàn)閟tatic沒有this指針,但是const修飾this指針,所以...
                 (11)如何修改常量?
                      有時(shí)候我們卻不得不對類內(nèi)的數(shù)據(jù)進(jìn)行修改,但是我們的接口卻被聲明了const,那該怎么處理呢?我對這個(gè)問題的看法如下:
                       1)標(biāo)準(zhǔn)用法:mutable
                          class A
                          {
                           public:
                                  A(int i=0):test(i)        { }
                                  void SetValue(int i)const { test=i; }
                           private:
                                  mutable int test;   file://這里處理!
                           };
                       2)強(qiáng)制轉(zhuǎn)換:const_cast
                           class A
                           {
                           public:
                                  A(int i=0):test(i)        { }
                                  void SetValue(int i)const
                                  { const_cast <int>(test)=i; }//這里處理!
                           private:
                                  int test;  
                           };
                        3)靈活的指針:int*
                           class A
                          {
                           public:
                                  A(int i=0):test(i)        { }
                                  void SetValue(int i)const
                                  { *test=i; }
                           private:
                                  int* test;   file://這里處理!
                           };
                        4)未定義的處理
                          class A
                          {
                           public:
                                  A(int i=0):test(i)        { }
                                  void SetValue(int i)const
                                  { int *p=(int*)&test; *p=i; }//這里處理!
                           private:
                                  int test;  
                           };
                            注意,這里雖然說可以這樣修改,但結(jié)果是未定義的,避免使用!
                         5)內(nèi)部處理:this指針
                          class A
                          {
                           public:
                                  A(int i=0):test(i)        { }
                                  void SetValue(int i)const
                                  { ((A*)this)->test=i; }//這里處理!
                           private:
                                  int test;  
                           };
                         6)最另類的處理:空間布局
                           class A
                           {
                            public:
                                  A(int i=0):test(i),c('a') {  }
                            private:
                                  char c;
                                  const int test;
                            };
                            int main()
                            {
                                A a(3);
                                A* pa=&a;
                                char* p=(char*)pa;    
                                int*  pi=(int*)(p+4);//利用邊緣調(diào)整
                                *pi=5;                 file://此處改變了test的值!
                                return 0;
                             }
                    雖然我給出了6中方法,但是我只是想說明如何更改,但出了第一種用法之外,另外5種用法,我們并不提倡,不要因?yàn)槲疫@么寫了,你就這么用,否則,我真是要誤人子弟了:)
                 (12)最后我們來討論一下常量對象的動(dòng)態(tài)創(chuàng)建。
                       既然編譯器可以動(dòng)態(tài)初始化常量,就自然可以動(dòng)態(tài)創(chuàng)建,例如:
                       const int* pi=new const int(10);
                       這里要注意2點(diǎn):
                       1)const對象必須被初始化!所以(10)是不能夠少的。
                       2)new返回的指針必須是const類型的。
                       那么我們可不可以動(dòng)態(tài)創(chuàng)建一個(gè)數(shù)組呢?
                       答案是否定的,因?yàn)閚ew內(nèi)置類型的數(shù)組,不能被初始化。
                       這里我們忽視了數(shù)組是類類型的,同樣對于類內(nèi)部數(shù)組初始化我們也做出了這樣的忽視,因?yàn)檫@涉及到數(shù)組的問題,我們以后再討論。

            posted @ 2007-10-26 08:31 寶杉 閱讀(203) | 評論 (0)編輯 收藏

                 摘要: ·                                 比...  閱讀全文

            posted @ 2007-09-04 10:46 寶杉 閱讀(423) | 評論 (0)編輯 收藏

            模板是重用機(jī)制的一種工具,可以實(shí)現(xiàn)類型參數(shù)化,即把類型定義為參數(shù),實(shí)現(xiàn)代碼可重用性。

             

            F:宏定義也可以實(shí)現(xiàn)重用,為什么不使用宏?

            Q:宏避開C++類型檢查機(jī)制,兩個(gè)不同類型參數(shù)之間的比較將會(huì)導(dǎo)致錯(cuò)誤。

             

            模板,模板函數(shù),模板類和對象之間的關(guān)系。

            模板

            (函數(shù)模板和類模板)

            模板函數(shù)

            模板類

            對象

            箭頭代表實(shí)例化

             

            模板

            (函數(shù)模板和類模板)

            模板函數(shù)

            模板類

            對象

             

            例如:

            C++exams\template_max

            模板形參將T實(shí)例化的參數(shù)。

            函數(shù)模板是一個(gè)不完全的函數(shù),首先需要將模板形參T實(shí)例化為確定的類型。這個(gè)類型可以是預(yù)定義的,也可以是用戶自定義。

             

            模板函數(shù)的異常

            1 先調(diào)用順序遵循什么約定?

            1)尋找類型完全匹配的函數(shù)。

            2)尋找函數(shù)模板,實(shí)例化后,產(chǎn)生匹配的模板函數(shù)。

            3)若(1)(2)失敗,嘗試低一級的對函數(shù)重載的方法。例如,通過類型轉(zhuǎn)換。

            4)(1)(2)(3)都為匹配,則是一個(gè)錯(cuò)誤。

             

            2 先了解了這樣的約定,就很容易理解以下:

            用非模板函數(shù)重載函數(shù)模板,只聲明非模板類函數(shù)的原型,不給出函數(shù)體,而函數(shù)體借用函數(shù)模板的函數(shù)體。

            例如:

            template <class T>

            T max( T x, T y)

            {

                     return( x > y )? x : y;

            }

            int max( int, int);

            //int max 重載了函數(shù)模板的函數(shù)體;

            int i;

            char c;

            maxi, c;

             

             

            3 定義一個(gè)完整的有函數(shù)體的非模板函數(shù)。

            比如:max(char, char)時(shí),上面的函數(shù)模板不能比較比一個(gè)字符更長的字符串了。

            所以,像一般的重載函數(shù)一樣定義。

            char *max( char *x, char *y )

            {

                     return ( strcmp(x, y) > 0 ) ? x y

            }

            char * max重載了函數(shù)模板,當(dāng)調(diào)用為:

            max ( “abcd”, “efgh” ); 將執(zhí)行此函數(shù)。

            posted @ 2007-08-30 10:47 寶杉 閱讀(224) | 評論 (0)編輯 收藏

            如果不想讓別人使用編譯器編寫構(gòu)造拷貝和賦值函數(shù),可以聲明為私有:

                 class A

                 {

                   private:

                     A(const A &a);                   // 私有的拷貝構(gòu)造函數(shù)

                     A & operate =(const A &a);  // 私有的賦值函數(shù)

                 };

             

            如果有人試圖編寫如下程序:

                 A  b(a); // 調(diào)用了私有的拷貝構(gòu)造函數(shù)

                 b = a;        // 調(diào)用了私有的賦值函數(shù)

            編譯器將指出錯(cuò)誤,因?yàn)橥饨绮豢梢圆僮?/span>A的私有函數(shù)。

            但是怎樣才能使用構(gòu)造拷貝和賦值函數(shù)呢?

            虛擬函數(shù)使用:C++exams\destructor

             

            在編寫派生類的賦值函數(shù)時(shí),注意不要忘記對基類的數(shù)據(jù)成員重新賦值。例如:

            C++exams\base_operator

            posted @ 2007-08-30 10:33 寶杉 閱讀(181) | 評論 (0)編輯 收藏

                 摘要: 定義:      class String      {        public:          String(const char *str = NULL);  ...  閱讀全文

            posted @ 2007-08-30 10:31 寶杉 閱讀(277) | 評論 (0)編輯 收藏

             

            析構(gòu)函數(shù)

            構(gòu)造函數(shù)初始化表:構(gòu)造函數(shù)特殊的初始化方式“初始化表達(dá)式表”(簡稱初始化表)。

            初始化表位于函數(shù)參數(shù)表之后,卻在函數(shù)體 {} 之前。這說明該表里的初始化工作發(fā)生在函數(shù)體內(nèi)的任何代碼被執(zhí)行之前。

            規(guī)則

            u       如果類存在繼承關(guān)系,派生類必須在其初始化表里調(diào)用基類的構(gòu)造函數(shù)。

            u       類的const常量只能在初始化表里被初始化,因?yàn)樗荒茉诤瘮?shù)體內(nèi)用賦值的方式來初始化。

            u       類的數(shù)據(jù)成員的初始化可以采用初始化表或函數(shù)體內(nèi)賦值兩種方式,這兩種方式的效率不完全相同。

            效率

            1 內(nèi)部成員:

            初始化表和函數(shù)體內(nèi)賦值都可以,但效率不完全相同,但后者更為清晰直觀。

            例子:

            class F

            {

             public:

                F(int x, int y);        // 構(gòu)造函數(shù)

             private:

                int m_x, m_y;

                int m_i, m_j;

            }

            F::F(int x, int y)

             : m_x(x), m_y(y)          

            {

               m_i = 0;

               m_j = 0;

            }

            F::F(int x, int y)

            {

               m_x = x;

               m_y = y;

               m_i = 0;

               m_j = 0;

            }

            示例9-2(c) 數(shù)據(jù)成員在初始化表中被初始化     示例9-2(d) 數(shù)據(jù)成員在函數(shù)體內(nèi)被初始化

            兩種方式效率區(qū)別不大。

             

            2 非內(nèi)部成員:

            只能用初始化表,提高效率。

            例子:

                class A

            {…

                A(void);                // 無參數(shù)構(gòu)造函數(shù)

                A(const A &other);      // 拷貝構(gòu)造函數(shù)

                A & operate =( const A &other); // 賦值函數(shù)

            };

             

                class B

                {

                 public:

                    B(const A &a); // B的構(gòu)造函數(shù)

                 private: 

                    A m_a;         // 成員對象

            };

            比較與分析:

            B::B(const A &a)

             : m_a(a)          

            {

               …

            }

            B::B(const A &a)

            {

            m_a = a;

            }

            1 B類構(gòu)造函數(shù)的初始化里,調(diào)用了A類的拷貝構(gòu)造函數(shù)。

            2 B類構(gòu)造初始化里,隱藏了以下幾個(gè)步驟:

            先創(chuàng)建了a對象,調(diào)用了A類的無參數(shù)構(gòu)造函數(shù);

            把a(bǔ)賦值給m_a,調(diào)用了A類的賦值函數(shù);

             

            深入探討:

            構(gòu)造和析構(gòu)的次序?

            構(gòu)造從最深處的基類開始的,先一層層調(diào)用基類的構(gòu)造函數(shù),然后調(diào)用成員對象的構(gòu)造函數(shù)。

            而析構(gòu)函數(shù)嚴(yán)格按照構(gòu)造函數(shù)相反的次序執(zhí)行,該次序唯一,以便讓編譯器自動(dòng)執(zhí)行析構(gòu)函數(shù)。

            特別之處是,成員對象初始化次序不受構(gòu)造函數(shù)初始化表次序影響,由在類中聲明的次序決定。而類聲明是唯一的,構(gòu)造函數(shù)卻可能有多個(gè),所以有多個(gè)不同次序函數(shù)初始化表。如果按照構(gòu)造函數(shù)的次序構(gòu)造,那么解析函數(shù)不能得到唯一的逆序。

            posted @ 2007-08-30 10:28 寶杉 閱讀(195) | 評論 (0)編輯 收藏

            #include <iostream>
            using namespace std;
            #include <stdio.h>

            #define OTL_ORA7 // Compile OTL 3.1/OCI7
            #include <otlv4.h> // include the OTL 4 header file

            otl_connect db; // connect object

            void insert()
            // insert rows into table
            {
             otl_stream o(50, // buffer size
                          "insert into test_tab values(:f1<float>,:f2<char[31]>)",
                             // SQL statement
                          db // connect object
                         );
             char tmp[32];

             for(int i=1;i<=100;++i){
              sprintf(tmp,"Name%d",i);
              o<<(float)i<<tmp;
             }
            }

            void select()
            {
             otl_stream i(50, // buffer size
                          "select * from test_tab where f1>=:f<int> and f1<=:f*2",
                             // SELECT statement
                          db // connect object
                         );
               // create select stream
             
             float f1;
             char f2[31];

             i<<8; // assigning :f = 8
               // SELECT automatically executes when all input variables are
               // assigned. First portion of output rows is fetched to the buffer

             while(!i.eof()){ // while not end-of-data
              i>>f1>>f2;
              cout<<"f1="<<f1<<", f2="<<f2<<endl;
             }

             i<<4; // assigning :f = 4
               // SELECT automatically executes when all input variables are
               // assigned. First portion of output rows is fetched to the buffer

             while(!i.eof()){ // while not end-of-data
              i>>f1>>f2;
              cout<<"f1="<<f1<<", f2="<<f2<<endl;
             }

            }

            int main()
            {
             otl_connect::otl_initialize(); // initialize OCI environment
             try{

              db.rlogon("scott/tiger"); // connect to Oracle

              otl_cursor::direct_exec
               (
                db,
                "drop table test_tab",
                otl_exception::disabled // disable OTL exceptions
               ); // drop table

              otl_cursor::direct_exec
               (
                db,
                "create table test_tab(f1 number, f2 varchar2(30))"
                );  // create table

              insert(); // insert records into table
              select(); // select records from table

             }

             catch(otl_exception& p){ // intercept OTL exceptions
              cerr<<p.msg<<endl; // print out error message
              cerr<<p.stm_text<<endl; // print out SQL that caused the error
              cerr<<p.var_info<<endl; // print out the variable that caused the error
             }

             db.logoff(); // disconnect from Oracle

             return 0;

            }

            posted @ 2007-08-20 17:01 寶杉 閱讀(689) | 評論 (2)編輯 收藏

            OTL stream concept

            Any SQL statement, PL/SQL block or a stored procedure call is characterized by its input / output [variables].

            Example 1. A SELECT statement has scalar input variables that are used in the WHERE clause of the statement. The SELECT statement also defines output columns. Potentially, the output columns are vector parameters since the SELECT statement may return multiple rows.

            Example 2. An INSERT statement writes data into a table, i.e. it has input parameters. Same is true for UPDATE statemements.

            Example 3. A DELETE statement deletes rows from a table. Deletion criteria needs to be entered, thus the DELETE statement has input.

            Example 4. A stored procedure may have input and/or output parameters. Usually, stored procedure parameters are scalars. There is a special case, though: stored procedure returning a referenced cursor (Oracle) or a result set (MS SQL Server or Sybase).

            Example 5. An arbitrary PL/SQL block may have input or/and output parameters that may be either scalars or vectors.

            Industrial strength database servers have bulk (or array) operations:

            • bulk INSERT
            • bulk UPDATE
            • bulk DELETE
            • bulk SELECT

            Therefore, parameters in INSERT/UPDATE/DELETE statement may be vectors if the statement is performed in bulk.

            The picture is clear: any interaction with SQL or its procedural extension can be treated as a black box with input and/or output. It does not matter what the black box does inside (according to the definition of a black box). What matters is the input wires that send signals into the box and the output wires that receive signals from the box:

            Some of the wires may be both input and output.

            Why not combine the concept of data streams and SQL? Instead of multiplying constructs and making database API's too convoluted, why not unify and simplify them? The OTL gives an answer to those questions and the answer is the otl_stream class.

            Since a SQL statement may be done in bulk, the otl_stream is a buffered stream. Conceptually, the otl_stream has two separate buffers: input and output. The input buffer is comprised of all input variables put together. Respectively, the output buffer is comprised of all output variables put together.

            C++ streams are usually manipulated via operator >> and operator <<. The stream reference is on the left of the operator symbol:

               s>>variable;
            s<<variable;

            The double arrow shows the direction in which data goes:

            • >> -- from the stream into the data container (variable)
            • << -- from the data container (variable) into the stream

            OTL streams are similar to buffered C++ streams . A SQL statement or stored procedure call is opened as an ordinary buffered stream. The logic of the OTL stream operations remains the same as the C++ stream operations with the only exception -- the OTL stream has separate input and output buffers which may overlap.

            The OTL stream has a flush function for flushing its input buffer when the buffer gets full and a collection of >> and << operators for reading and writing objects of different data types. The most important advantage of the OTL streams is their unified interface to SQL statements and stored procedure call of any kind. This means that the application developer needs to remember just a few syntactical constructs and function names which he already got familiar with when he started working with C++ streams.

            Inside the OTL stream there is a small parser for parsing declarations of bind variables and their data types. There is no need to declare C/C++ host variables and bind them with placeholders by special bind function calls. All necessary buffers are created dynamically inside the stream. The stream just needs to be opened for reading and writing values.

            The OTL stream interface requires use of the OTL exceptions. This means that potentially any OTL stream operation can throw an exception of the otl_exception type. In order to intercept the exception and prevent the program from aborting, wrap up the OTL stream code with the corresponding try & catch block.

            The functioning of the otl_stream is pretty much automatic: when all of the input variables of the stream are defined (in other words, the input buffer is filled out), it triggers the block box inside the stream to execute. The output buffer gets filled out in the process of the execution of the black box. After the execution is finished, the output values can be read from the stream. If it is a SELECT statement and it returns more rows than the output buffer can hold, after the whole output buffer is read, then the stream automatically fetches the next bacth of rows into the output buffer.

            posted @ 2007-08-20 16:58 寶杉 閱讀(2558) | 評論 (4)編輯 收藏

            Introduction

            This document describes the Oracle, Odbc and DB2-CLI Template Library, Version 4.0 (OTL 4.0). OTL 4.0 is a C++ library based on templates. It integrates all of the previous releases into one library.

            OTL 4.0 was designed as a combination of a C++ template framework and OTL-adapters. The framework is a generic implementation of the concept of OTL streams. The OTL-adapters are thin wrappers around the database APIs and are used as class type parameters to be substituted into the template framework.

            OTL 4.0 covers the functionality of a whole database API with just a handful of concrete classes: otl_stream, otl_connect, otl_exception, otl_long_string, and several template PL/SQL (Oracle) table container classes, generated from the template framework and the OTL-adapters.

            The OTL code gets expanded into direct database API function calls, so it provides ultimate performance, reliability and thread safety in multi-processor environments as well as traditional batch programs. OTL 4.0, being a template library, is highly portable since it is self-sufficient and compact enough.

            OTL 4.0 is ANSI C++ compliant (ANSI C++ typecasts, clean templatized code, etc.), tightly integrated with the Standard Template Library (STL) via so-called STL-compliant stream iterators, and natively supports the STL std::string's in otl_stream's.

            The current version of the OTL supports Oracle 7 (natively via OCI7), Oracle 8 (natively via OCI8), Oracle 8i (natively via OCI8i), Oracle 9i (natively via OCI9i), Oracle 10g (natively via OCI10g), DB2 (natively via DB2 CLI), ODBC 3.x as well as ODBC 2.5 compliant data sources in MS Windows and Unix (e.g. Oracle, MS SQL Server, Sybase, Informix, MySQL, DB2, Interbase / Firebird, PostgreSQL, SQLite, SAP/DB, TimesTen, MS ACCESS, etc.). The list of supported database backends is constantly growing.

            posted @ 2007-08-20 16:55 寶杉 閱讀(919) | 評論 (0)編輯 收藏

            內(nèi)聯(lián)函數(shù):既增加安全性,又可以調(diào)用類的數(shù)據(jù)成員。

            C++中,用內(nèi)聯(lián)取代所有宏,但在Debug版本中,assert是例外。

            assert不產(chǎn)生任何副作用,調(diào)用函數(shù)會(huì)引起內(nèi)存、代碼的變動(dòng),所以assert是宏。

            內(nèi)聯(lián)格式:

                 void Foo(int x, int y);    

                 inline void Foo(int x, int y)    // inline與函數(shù)定義體放在一起

                 {

                    

                 }

            用于實(shí)現(xiàn)的關(guān)鍵字,而非用于聲明的關(guān)鍵字。

            在類中的規(guī)范格式:

            class A

                 {

            public:

                     void Foo(int x, int y)

                 }

                 // 定義文件

                 inline void A::Foo(int x, int y)

            {

            }

            什么情況不適合使用內(nèi)聯(lián)?

            1 內(nèi)聯(lián)函數(shù)代碼較長,導(dǎo)致內(nèi)存消耗較高。

            2 內(nèi)聯(lián)函數(shù)包括循環(huán),執(zhí)行內(nèi)聯(lián)函數(shù)代碼比調(diào)用函數(shù)的開銷大。

            F:為什么構(gòu)造和解析函數(shù)不使用內(nèi)聯(lián)?

            Q:因?yàn)槲鰳?gòu)函數(shù)可能“隱藏”一些行為,例如執(zhí)行基類或成員對象的析構(gòu)過程。

            有時(shí)候編譯器會(huì)自動(dòng)取消一些不值得的內(nèi)聯(lián),所以在聲明時(shí)不寫inline是合理的。

            posted @ 2007-08-13 10:24 寶杉 閱讀(214) | 評論 (0)編輯 收藏

            僅列出標(biāo)題
            共4頁: 1 2 3 4 
            精品久久久久久久中文字幕| 精品无码久久久久久国产| 免费一级欧美大片久久网| 久久国产亚洲精品麻豆| 久久久久久国产精品免费无码 | 狠狠色丁香婷婷久久综合不卡| 国产亚洲精久久久久久无码77777| 久久九九久精品国产| 国产精品成人久久久久久久| 伊人丁香狠狠色综合久久| 一本久久久久久久| 久久综合丝袜日本网| 好属妞这里只有精品久久| 丁香五月网久久综合| 久久中文字幕一区二区| 91精品国产91久久| 国内精品久久久久久久涩爱| 久久人人超碰精品CAOPOREN| 波多野结衣久久| 99精品国产99久久久久久97 | 99热精品久久只有精品| 色综合久久中文综合网| 品成人欧美大片久久国产欧美...| 精品多毛少妇人妻AV免费久久 | 人妻无码αv中文字幕久久琪琪布| 色狠狠久久综合网| 人妻无码αv中文字幕久久| 2021少妇久久久久久久久久| 人人狠狠综合久久亚洲88| 欧美色综合久久久久久| 欧美日韩久久中文字幕| 精品久久久久久无码专区不卡| 午夜不卡888久久| 国产精品99久久久精品无码| 77777亚洲午夜久久多喷| 国内精品免费久久影院| 久久精品国产亚洲AV蜜臀色欲| 97久久精品人妻人人搡人人玩| 国产综合精品久久亚洲| 亚洲中文字幕久久精品无码喷水 | 国产免费久久久久久无码|