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

            山寨:不是最好的,是最適合我們的!歡迎體驗山寨 中文版MSDN

            Blog @ Blog

            當華美的葉片落盡,生命的脈絡才歷歷可見。 -- 聶魯達

            常用鏈接

            統計

            積分與排名

            BBS

            Blog

            Web

            最新評論

            [轉]談談c++的初始化工作

            關于c中的初始化相關部分,如指針,如全局變量與局部變量默認初始化的區別,如靜態變量的默認初始化,就跳過。我們從類開始。
                初始化是非常重要的工作,因為你的類(確切說是對象,程序)的執行過程就是一系列狀態變換,而初態不正確,就不可能到達正確解了。
                面向對象的c++中的初始化工作,是由構造函數來完成的,在其他場景可能稱為構造器。這是大家都明白的。但是,展開來,或許您還未必清楚,如,如何設計好的默認初始化,哪些成員變量只有唯一的初始化形式,組合與繼承的初始化,資源淺拷貝問題,無名對象的問題,特殊需要的初始化(實例對象須唯一化)等等。我將在vc7.0上調試程序,每次調試一個,談一個問題,試圖給您解釋清楚。愿于您有所幫助。

                這次就說說好的初始化過程與靜態成員的初始化。
                不管程序員如何,面向對象的c++中初始化工作是必須的!!你寫了一個類,沒有寫構造函數,但是,系統會“暗暗的”給你一個系統默認的構造函數,在實例化對象的時候它就會工作---要知道,一旦你自己定義了構造函數,系統就不會再提供默認構造函數。
                問題是,我們應該定義自己的構造函數。否則,系統多半是無法達到正確的初始狀態的!
                定義好的構造函數,應該是給出多版本的構造函數,作好安全檢查工作。我們下面給出一個例子,由c++締造者的例子改動邇來。
              
                 需要一個類,日期Date,它有成員變量day,month,year,執行一些相關操作。如何進行初始化工作?我們或許會見到下面的代碼:

             

                //
                class Date {
                    int d
            ,m,y;
                public:
                    Date(int dd
            =0,int mm=0,int yy=0)
                    {
                       d
            =dd;
                       m=mm;
                       y=yy;
                    }

                 //
                }
            ;
                //

             

                這樣的程序沒有語法錯誤,可以工作,但不是正確工作。下面這個語句會怎么樣呢?
                 
                Date oneday(-2,10,2002);
                作簡單的檢查,如下面的代碼部分。也是于事無補的。如對下面的語句仍然是無能為力的:

                

            Date oneday(29,2,1981);

                //
                class Date {
                    int d
            ,m,y;
                public:
                    Date(int dd
            =0,int mm=0,int yy=0)
                    {
                      if(dd>
            =0&&yy>=0&&mm>=0&&m<=31){//???
                          d
            =dd;
                          m=mm;
                          y=yy;
                       }
                       //else ???
                    }

                 //
                }
            ;
                //

                更何況,我們可能會需要用string來初始化,用char *指針來初始化:
                string s="29/2/1981";
                char *p="29/2/1981";

                應該怎么辦呢?我想你有必要好好審視你的初始化工作了?。。?/p>

                 我們來看一個設計實例:

            Date oneday(29,2,1981);

                //
                class Date {
                    int d
            ,m,y;
                public:
                    Date(int dd
            =0,int mm=0,int yy=0)
                    {
                      if(dd>
            =0&&yy>=0&&mm>=0&&m<=31){//???
                          d
            =dd;
                          m=mm;
                          y=yy;
                       }
                       //else ???
                    }

                 //
                }
            ;
                //

                 我們來看看實現部分:

             

            //date.cpp
            #include 
            "date.h"
            #using <mscorlib.dll>

            //靜態成員的初始化
            Date Date::default_date(
            4,feb,1981);

            Date::~Date(void)
            {
            }
            //詳盡的初始化工作的例子
            Date::Date(int dd
            , Month mm, int yy)
            {
                   //(
            1
             if(dd
            ==0) dd=default_date.day();//test d=default_date.day()
             if(mm==0) mm=default_date.month();//test m=default_date.month()
             if(yy==0) yy=default_date.year();//test y=default_date.year()
                     int max;

             switch(mm)
             {
             case feb:
              max
            =28+leapyear(yy);
              break;
             case apr:case jun:case sep:case nov:
              max
            =30;
              break;
             case jan:case mar:case may:case jul:case aug:case oct:case dec:
              max
            =31;
              break;
             default:
              throw Bad_date()
            ;
             }
             if(dd<
            1||max<dd||yy<0) throw Bad_date();

             y
            =yy;
             m=mm;
             d=dd;
            }

            void Date::set_default(int d
            , Month m, int y)
            {
             Date::default_date
            =Date(d,m,y);
            }

            int Date::day(void) const
            {
             return d
            ;
            }

            Month Date::month(void) const
            {
             return m
            ;
            }

            int Date::year(void) const
            {
             return y
            ;
            }
            //測試函數
            void Date::Test(void)
            {
             std::cout<<
            "\n This is a test using class Date. \n"
              <<
            " The date is(day/month/year) :"<<d<<"/"<<m<<"/"<<y
                      <<std::endl
            ;
             std::cout<<"\n Thank you! \n\n";
            }

             

                這里,有幾個需要注意的,就是:
                (1)構造函數的版本
                     Date(int dd=0, Month mm=Month(0), int yy=0);
                     Date(string s) { /* 省去內容*/}
                     Date(char *p) { /*省去內容*/}
                (2)靜態成員提供默認的值
                     //靜態成員變量
             static Date default_date;
                     //及接口
                     static void set_default(int d, Month m, int y);
                (3)異常管理
                    //異常類(默認構造函數,因為我們只是拋出異常,甚至沒有標志)
             class Bad_date{};
                (4)構造函數中較好的算法
               
                這些都是我們初始化工作交好的保證!
                用下面的文件程序測試,可得結果:

            //fmain.cpp
            #include 
            "date.h"

            void main()
            {
                Date oneDay
            ;
             oneDay.Test();
            }
            /*結果:

             This is a test using class Date.
             The date is(day/month/year) :
            4/2/1981

             Thank you!

            Press any key to continue
            */

             

            下面回到實現程序文件date.cpp,看(1)部分的代碼。我后面注釋了三行的代碼。如果我用注釋的代碼換掉程序中的代碼,您覺得會出現什么結果?
            我們首先來看上次遺留的問題。
            把(1)中的代碼換為注釋部分,或許您一時還認識不到會有什么發生,但最終是通不過的,調試拋出異常,信息如下:
            未處理的“System.Runtime.InteropServices.SEHException”類型的異常出現在 TestInit.exe 中

            其他信息:外部組件發生異常。

            This application has requested the Runtime to terminate it in an unusual way.
            Please contact the application's support team for more information.
            Press any key to continue

            我想,您回頭再細看的話,就會明白為什么如此了(我們寫程序一定要追問到底:)。

            我們今天要談的是,一些變量只有唯一的初始化形式,通過例子,告訴您要特別注意。然后,我們就一步一步,來看資源淺拷貝的問題。我相信初學c++的同學,會對“拷貝函數”有些疑問,它就是為了解決上述問題的;但事實上,還有一個隱藏的地方,今天我也想給您指出。
            這些程序,可是我特意設計的哦。希望可以很方便的認識問題所在,與解決之道。

            首先,看第一個例子。在類中,這兩類變量:
            e.g.
            Name &name;    //引用
            const int ID;  //常量
            它們的初始化形式是唯一的。而且必須由您來初始化。
            看下面的程序:
             

            //human.h
            #pragma once

            class Name
            {
                char *name
            ;
            public:
               //
            }
            ;
            class Human
            {
             Name &name
            ;
             const int ID;//每個人都唯一的標志號
             //  
            public:
             Human(void)
            ;
             ~Human(void);
             
             //
            }
            ;
            //human.cpp
            #include 
            "human.h"
            #using <mscorlib.dll>

            //默認的構造函數
            Human::Human(void)
            {
            }

            Human::~Human(void)
            {
            }

            寫一個主文件測試。
            但調試出錯,錯誤信息文件為:
            /*----------------------------------------------------------------------------
            //Human:error file
            ------ 已啟動生成:項目:TestInit, 配置:Debug Win32 ------

            正在編譯...
            Human.cpp
            Human.cpp(5) : error C2758: “Human::name” : 必須在構造函數基/成員初始值設定項列表中初始化
                    e:\NET\Small_code\TestInit\Human.h(13) : 參見“Human::name”的聲明
            Human.cpp(5) : error C2758: “Human::ID” : 必須在構造函數基/成員初始值設定項列表中初始化
                    e:\NET\Small_code\TestInit\Human.h(14) : 參見“Human::ID”的聲明
            fmain.cpp
            Date.cpp
            正在生成代碼...

            生成日志保存在“file://e:\NET\Small_code\TestInit\Debug\BuildLog.htm”中
            TestInit - 2 錯誤,0 警告


            ---------------------- 完成 ---------------------

                生成:0 已成功, 1 已失敗, 0 已跳過
            --------------------------------------------------------------------------------
            */
               
                因為這里涉及的是僅僅的c++語法,我就不多費口舌了,如何改正,希望您能動手試試,一定要動手,不要想當然哦~~~
                當然,如果您是愛問題的人,我想您可以這樣深究一下:設計c++語言時,為什么諸如int類型成員變量能提供默認初始化,而它們卻不能;從編譯角度,刻意給它們提供如int類型般的初始化會有什么困難和問題?


               下面詳細談什么是資源淺拷貝問題。沿襲c的習慣,c++對系統自分配的資源進行統一管理,但是,用戶申請的資源,則有用戶來釋放。
               比如:

                   userType *p=new userType(/*---*/);
                   //...
                   delete p;
                   //delete釋放一般是不可忘的

                單獨的變量或許對您來說是不成問題的。但在類中,這些情況就變的相當復雜。處理不好,您的系統要么就是因為內存泄露而運行不下去,而要么就是異常頻頻發生。
                我們先來看一些c++的默認操作:
                //...
                class OneClass {
                    int _value;
                public:   
                    OneClass(int _val=0):_value(_val) {}
                    ~OneClass() {}

                    //...
                };

                //you may use in this way:
                OneClass oneObj(7);
                OneClass anotherObj;
                anotherObj=oneObj;//(1)
                //...
                //int Compare(OneClass one,OneClass two);
                int k=Compare(oneObj,anotherObj);//(2)
                //...

                在本程序的場景下,上面的代碼是可以完好工作的,但您清楚(1)與(2)系統都作了什么了嗎?您是否知道,如果您的初始化工作做的不好的話,即使,就沿用上面的初始化習慣,您的程序很容易就崩潰了呢。
                答案是,(1)語句執行時,默認的,系統試圖把oneObj的資源全部copy給anotherObj,但用戶申請的資源(new來的:),卻傳入的是地址;(2)語句的默認形參傳遞遵循同樣的規則。
                當然這與java與c#是不同的,因為java與c#的對象是引用類型。而c++,除非您強行定義為引用類型的,它就不是。

                我們來看下面的例子,第一遍我建議您只看程序,不要往下看,看您能否發現什么問題。

             

            //human.h    
             #pragma once

            #define NULL 
            0

            class Name
            {
                char *name
            ;
            public:
             Name(char *_name
            =NULL):name(_name) {}
             ~Name() { }

             char *getName(){ return name
            ;}

            }
            ;
            class Human
            {
             Name *name
            ;//
             int ID;    //唯一化標志
            public:
             Human(int id
            =0,char *_name=NULL);
             ~Human(void);

             int getID()const { return ID
            ;}
             Name *getName() { return name;}
            };

            //human.cpp
            #include 
            "human.h"
            #using <mscorlib.dll>

            Human::Human(int id
            ,char *_name):ID(id)
            {
               name
            =new Name(_name);//初始化:指針
            }

            Human::~Human(void)
            {
             delete name
            ;//析構時釋放資源
            }

            //fmain.cpp
            #include <iostream>
            #include 
            "human.h"

            void main()
            {       
                   //測試程序
             try{
             Human lily(
            11100120,"lily");
             Human lucy=lily;
             }
             catch()
             {//如果有any異常
              std::cout<<
            "\n Unknown Exception\n";
             }
             
            }   

             

            //請回頭看程序,您覺得一切都好嗎?


                事實上,調試過程中,等三個異常忽略后,就會得到下面的結果:
            /*   
            After three exceptions occured you get :

             Unknown Exception...
            Press any key to continue

            */
                為什么?
                看這幾行代碼:
                    Human lily(11100120,"lily");
             Human lucy=lily;
                雖然一開始,我就給了小例子,形式一樣,那個沒問題。何以這個就不行了呢?因為類的定義不同。
                由c++工作的機理,這行
                   Human lucy=lily;
                是把lily的資源拷貝給lucy(lucy是不調用構造函數的),可是,因為其中的name是用戶申請的資源,并不能把它也拷貝過去,而是直接傳了地址。這樣,您知道嗎,lucy.name和lily.name的地址是一樣的。
                于是,當一個的析構函數調用后,name所指向的資源已被釋放掉了的。而另外一個類的析構函數又去釋放,問題來了---程序崩潰了!
               
                這就是淺拷貝問題---“淺”的不完全的拷貝:)。
               
                找到原因,我們就辦法。解決的辦法是,這份工作自己來做!
                寫一個拷貝賦值操作(public):

                形式為:   className &operator=(className &obj){ /*...*/}
                您看下面的解決辦法和結果:

            /*

            If you add in class Human(public):

             Human &operator
            =(Human &human){
              if(this!
            =&human){
                  ID
            =human.getID();
                  name=new Name(human.getName()->getName());
                  return *this;
              }
             }
             
             
            OK
            ,and you get:

            Press any key to continue

            That is what we want!
            */



                下面的例子,是拷貝函數的問題,在上面的基礎上,我改動了一下程序的結構,
            定義了一個名空間。
                具體的問題分析我留給下次,您就有機會細細的看了。您是否一切都清楚了?
            您可以解決這個問題嗎?

             

            //human.h
            #pragma once
            #using <mscorlib.dll>
            namespace Humanbeing
            {
            #define NULL 
            0

            class Name
            {
                char *name
            ;
            public:
             Name(char *_name
            =NULL):name(_name) {}
             ~Name() { }

             char *getName(){ return name
            ;}

            }
            ;
            class Human
            {
             Name *name
            ; //
             int ID;     //唯一的標志
            public:
             Human(int id
            =0,char *_name=NULL):ID(id)
             {
              name
            =new Name(_name);//申請資源
             }
             ~Human(void)
             {
              delete name
            ;//釋放資源
             }

                    //拷貝賦值操作
             Human &operator
            =(Human &human){
              if(this!
            =&human){
                  ID
            =human.getID();
                  name=new Name(human.getName()->getName());
                  return *this;
              }
             }
             
             int getID()const { return ID
            ;}
             Name *getName() { return name;}
            };

            //名空間里的函數
            bool IsSameMan(Human one
            ,Human another)
            {
                if(one.getID()
            ==another.getID())
              return true
            ;
             else return false;
            }

            }

            //測試文件
            #include <iostream>
            #include 
            "human.h"
            void main()
            {
             using namespace Humanbeing
            ;
             try{
             Human lily(
            11100120,"lily");
             Human lucy=lily;

             if(IsSameMan(lucy
            ,lily))
             {
              std::cout<<
            "\n They are the same one.\n";
             }else 
              std::cout<<
            "\n No,they're not the same one.\n";

             }
             catch()
             {
              std::cout<<
            "\n Unknown Exception\n";
             }
             
            }

             


                 調試結果呢,是連續六個異常后,出現:
            After six exceptions occured you get :

             They are the same one.

             Unknown Exception...
            Press any key to continue

                 為什么(上次異常是三個,這次是六個,可以解釋嗎)?怎么辦?

            posted on 2007-08-08 16:40 isabc 閱讀(659) 評論(0)  編輯 收藏 引用 所屬分類: C++基礎

            廣告信息(免費廣告聯系)

            中文版MSDN:
            歡迎體驗

            青青草原综合久久| 国产福利电影一区二区三区久久久久成人精品综合 | 精品久久久久久中文字幕大豆网| 中文字幕久久精品| 国内精品久久久久影院一蜜桃| 久久最近最新中文字幕大全| 午夜精品久久久久9999高清| 久久亚洲精品成人AV| 久久久久亚洲精品天堂久久久久久| 国内精品综合久久久40p| 国产精品欧美亚洲韩国日本久久| 亚洲国产精品高清久久久| 狠狠色伊人久久精品综合网 | 国产精品一久久香蕉国产线看观看| 久久亚洲AV无码精品色午夜 | 亚洲人成伊人成综合网久久久| 日韩亚洲国产综合久久久| 色综合久久天天综线观看| 丁香色欲久久久久久综合网| 久久精品中文字幕无码绿巨人| 国产精品久久网| 一级a性色生活片久久无| 久久人妻少妇嫩草AV无码专区| 韩国无遮挡三级久久| 久久久久国色AV免费观看| 国色天香久久久久久久小说| 久久精品国产福利国产秒| 亚洲国产成人精品91久久久| 久久精品国产亚洲av水果派| 久久国产热这里只有精品| 久久国语露脸国产精品电影| 久久精品国产影库免费看| 久久毛片一区二区| 亚洲天堂久久精品| 国产成年无码久久久免费| 丁香久久婷婷国产午夜视频| 99久久精品免费看国产一区二区三区| 国产精品青草久久久久婷婷 | 狠狠精品久久久无码中文字幕 | 久久成人小视频| 91精品国产91久久|