C++你可能不知道地方

來源:互聯網
上載者:User
c++中編譯器替我們完成了許多事情,我們可能不知道,但也可能習以為常。下面詳細介紹  一、初始化與初始賦值首先說說類的初始化與初始賦值之前的區別,這也許裡面可能有我們不知道的事情。  其實類初始化與初始賦值還是有區別的。

 1         class People{ 2         public: 3                 People(std::string name,int age,int height); 4         private: 5                 std::string m_sName; 6                 int m_iAge; 7                 int m_iHeight; 8         } 9         //賦值10         People::People(std::string name,int age,int height)11         {12                 m_sName=name;13                 m_iAge=age;14                 m_iHeight=height;15         }16         //初始化列表17         People::People(std::string name,int age,int height)18         :m_sName(name),m_iAge(age),m_iHeight(height)19         {}
 C++規定,對象的成員變數初始化動作發生在進入建構函式本體之前。在建構函式內成員變數賦值都不是初始化,而是賦值。 賦值時首先調用預設建構函式為m_sName,m_iAge,m_iHeight賦初始值,然後在立刻調用賦值操作符進行賦新值。 成員初始列表是將各個成員變數實參都作為複製建構函式的實參。 所以看出賦值相對於初始化,多了一步就是使用賦值操作符進行賦值。所以初始化的效率比賦值的效率高多了。但是對於內建類型,它們效率是一樣的。  二、空類  想想你如果聲明一個空類,C++編譯器會對它做什麼呢?編譯器就會為它聲明一個複製建構函式,賦值操作符和一個解構函式,以及預設建構函式。所有這些函數都是public而且inline函數。編譯器寫的賦值建構函式和賦值操作符,只是單純地將來來源物件的每個non-static變數拷貝到目標對象,具體是進行位拷貝。     如果聲明了一個建構函式,編譯器是不會建立預設建構函式。  如果不希望類支援拷貝建構函式與賦值操作符怎麼辦?不聲明?按照上面說明編譯器會自動幫你產生。那麼可以將它們聲明為private,這樣阻止編譯器自動產生拷貝建構函式(public)。private成功阻止他人使用,但是這並不安全。因為類成員函數以及友元函數還是可以調用private的拷貝建構函式和賦值操作符。     如果只在private下聲明拷貝函數和賦值操作符,在有人通過類成員函數去以及member函數去調用它,會獲得一個串連錯誤。那麼這裡能不能將錯誤在編譯的時候體現出來呢?這裡只用將拷貝函式宣告為private,並且不在自身,就可以辦到了。顯然繼承一個拷貝函數和賦值操作符為private的基類就辦到了,基類如下:

1         class NonCopyable{2         protected:3                  NonCopyable (){}4                 ~  NonCopyable (){}5         private:6              NonCopyable (const  NonCopyable &);7              NonCopyable & operater=(const  NonCopyable &);8         };

  原因是類成員函數或者友元函數嘗試拷貝對象,編譯器便會嘗試產生一個複製建構函式與賦值操作符,並會調用基類的對應函數,但是會被拒絕,因為基類這些函數是private。 3、++函數  下面說說“*++"與"++*"中你不知道的事情,c++規定尾碼形式自加函數有一個int型別參數,當函數被調用時,便其一傳遞一個0作為int參數的值傳遞給該函數,而首碼形式自己函數,型別參數沒有要求,所以這樣就能區分一個++函數是首碼形式與尾碼形式了,具體代碼如下:

 1 class UPInt{ 2 public 3     UPInt& operator++( ) ;                      //++ 首碼 4     const UPInt operator++( int );          //++尾碼 5     UPInt& operator --( );                       // --首碼 6     const UPInt operator --( int )           //--尾碼 7     UPInt& operator +=( int );               // 8     ... 9 };10 11 UPInt & UPInt::operator++( )12 {13     *this += 1;14     return *this;15 }16 17 const UPInt UPInt :: operator++( int )18 {19     UPInt oldValue = *this;20     ++(*this);21     return oldValue;22 }

尾碼函數使用返回參數類型const,是為了避免下面代碼生效

1 UPInt i;2 i++++;

這個時候第一次調用++返回cosnt對象,並再次調用然後這個函數是non-const成員函數,所以const對象無法調用這個函數,那麼i++++就無法生效了。這裡說說效率問題,我們可以看到尾碼++函數建立一個臨時對象以作為它傳回值,這個臨時對象經過構造並在最後被析構。而首碼++函數沒有這樣的臨時變數,並且沒有那樣的操作。所以如果我們在程式中使用首碼++效率會更加高一些,沒有了臨時變數的構造與析構的動作。 4.虛解構函式帶有多態性質的base class應該聲明一個virtual解構函式。為什麼這麼說呢?看下面例子

        class base        { ... }        class derived:public base        {... }                base * p= new derived;
     假設這裡基類的解構函式不是virtual,當使用完p指標,我們刪除它的時候,想想會發生什麼,因為基類的解構函式是non-virtual所以不會發生多態直接調用基類解構函式,僅僅刪除繼承類中基類那部分內容,那麼繼承類對象其他記憶體沒有被銷毀,從而資源泄漏。    如果將其聲明為virtual,那麼就會發生多態,調用的是指向繼承類的指標,那麼就會銷毀的是整個繼承類象。  5.傳遞方式用引用 預設情況下c++以值傳遞方式傳遞對象至函數。函數參數都是以實際實參的複件為初值,而調用端所獲得的是函數傳回值的一個附件。這些複件都是由拷貝建構函式產出。看如下例子

 1         class Person{ 2         public: 3             Person(); 4             virtual ~Person(); 5             ... 6         private: 7             std::string name; 8             std::string address; 9         }10         11         class Student:public Person{12         public:13             Student();14             ~Student();15             ...16         private:17             std::string schoolName;18             std::string schoolAddress;19         };

 那麼如果有一個函數驗證是否為學生

1   bool validateStudent(Student s);2   Student plato;3   bool platoIsOK=validateStudent(plato);
  分析這3行代碼,編譯器到底做了什嗎?首先調用Student的copy建構函式,然後以plato為藍本將s初始化,當validateStudent返回被銷毀,所以成本為"一次Student copy建構函式調用,加上一次Student解構函式調用"。      Student對象內部有兩個string對象,所以構造了兩個string對象。Student繼承自Person對象,裡面又有兩個string對象。所以by value方式傳遞一個Student對象,總體成本是"六次建構函式和六次解構函式"!
      以by reference方式傳遞參數也可避免對象切割問題。當一個derived class對象以by value方式傳遞並被視為一個base class對象,base class的copy建構函式會被調用,造成像derived class對象全被切割掉了,僅僅留下base class對象。看如下代碼通過傳遞引用參數完成多態

        class Window{        public:            ...            std::string name() const;            virtual void display() const;        };        class WindowWithScrollBars:public Window{        public:            ...            virtual void display() const;        };                //傳入Windos類型,調用其display函數        //傳入WindowWithScrollBars類型,調用其display函數        //體現多態        void printNameAndDispaly(const Window& w)        {            std::cout<<w.name();            w.display();        }    

窺視c++編譯器的底層,reference往往以指標實現出來,因此pass by reference真正傳遞的是指標。如果對象屬於內建型,pass by value往往比pass by reference 效率高些。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.