程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++和C的區別與發展總結

C++和C的區別與發展總結

編輯:關於C++

之前C++學得就不太扎實,正好看到《VC++深入詳解》第二章對C++進行了一個簡單總結,故整理回顧之。


1.C++與C相比的特性


封裝性
封裝性把數據與操作數據的函數組織在一起,不僅使程序結構更加緊湊,並且提高了類內部數據的安全性。 繼承性
繼承性增加了軟件的可擴充性及代碼重用性。 多態性
多態性使設計人員在設計程序時可以對問題進行更好的抽象,有利於代碼的維護和可重用。

2.從結構到類


C++中不管是結構還是類,都可以包含函數。區別是:
類的關鍵字是class ; 結構的關鍵字是struct。 成員的訪問控制方面:
結構體默認情況下,其成員是公有(public)的; 類默認情況下,其成員是私有(private)的; 在一個類中,公有成員是可以在類的外部進行訪問的,而私有成員就只能在類的內部進行訪問

例如下面的程序就會報錯,因為x和y都是默認的私有成員,只有在類的內部才能進行訪問


5
5

那麼為什麼輸出不是10, 10呢,因為在input函數中,point類的成員變量x和y都是不可見的。並不是說成員變量在成員函數中不可見,而是如果成員函數中定義了和成員變量相同的變量,則成員變量在該成員函數中不可見,在下一個例子中可以說明這一點。


例二
5
10

在這個例子中,從輸出x=5,y=10可以看出:成員變量y在成員函數input中就是可見的, 成員變量x在成員函數input中是不可見的。

那麼該如何利用成員函數給成員變量賦值呢,可以用下面兩個方法:


例三
10
10

例四
利用this指針,this指針是一個隱藏的指針,它指向對象本身,代表了對象的地址。 例如上面幾個例子中的對象pt,this=&pt。 所有對數據成員的訪問都隱含地被加上了前綴this->,例如,x=0,等價於this->x=0。

所以我們可以這樣寫這個程序:


10
10


7.類的繼承



繼承


先看下面這個例子:


animal construct
fish construct
fish destruct
animal destruct

可以看出,在聲明子類對象時,父類的構造函數先運行,然後子類的構造函數,在對象聲明周期結束時,子類的析構函數先運行,然後是父類的析構函數。


在子類中調用父類的帶參數的構造函數



animal breathe


例二
fish bubble

為什麼例一中可以直接將fish類的對象fh的地址賦值給animal類的指針變量pAn?
因為fish對象也是一個animal對象,對fish類型轉換為animal類型不用強制類型轉換,C++編譯器會自動進行這種轉換。反過來,則不能把animal對象看成fish對象

為什麼例一的輸出結果為“animal breathe”而不是“fish bubble”呢?

因為我們將fish類的對象fh的地址賦值給pAn時,C++編譯器進行了類型轉換,此時C++編譯器認為變量pAn保存的就是animal對象的地址。 fish類對象所占的內存圖如下所示,它分為兩部分。
animal的對象所占內存; fish的對象自身增加的部分。 當我們將fish類的對象轉換為animal類型時,該對象就被認為是原對象整個內存模型的上半部分,也就是“animal的對象所占內存”。當我們利用類型轉換後的對象指針去調用它的方法時,自然也就是調用它所在的內存中的方法。

用virtual關鍵字申明的函數叫做虛函數。

對於例二輸出為“fish bubble”,這就是C++的多態性。當C++編譯器在編譯的時候,發現animal類的breathe()是虛函數,這時C++就會采用遲綁定技術。也就是編譯時不確定具體調用的函數,而是在運行時,依據對象的類型(在程序中,我們傳遞的是fish類對象的地址)來確認調用的是哪一個函數,這種能力叫做C++的多態性。我們沒有在breathe()函數前加virtual關鍵字時,C++編譯器在編譯時就確定了哪個函數被調用,這叫做早期綁定

純虛函數



class animal
{
public:
    void eat()
    {
        cout << "animal eat" << endl;
    }

    void sleep()
    {
    cout << "animal sleep" << endl;
    }

    virtual void breathe() = 0;
};

純虛函數是指被標明為不具體實現的虛成員函數。 純虛函數可以讓類先具有一個操作名稱,而沒有操作內容,讓派生類在繼承時再去具體地給出定義。 凡是含有純虛函數的類叫做抽象類,這種類不能聲明對象,只能作為基類為派生類服務。 在派生類中,必須完全實現基類的純虛函數,否則派生類也成了抽象類,不能實例話對象。

9.函數的覆蓋和隱藏



覆蓋


構成函數覆蓋的條件:

基類函數必須是虛函數; 發生覆蓋的兩個函數分別位於基類和派生類中; 函數名稱與參數列表必須完全相同。

例:


class animal
{
public:
    ...
    virtual void breathe()
    {
        cout << "animal breathe" << endl;
    }
    ...
};

class fish:public animal
{
public:
    ...
    void breathe()
    {
        cout << "fish bubble" << endl;
    }
    ...
};

上述例子中,fish類中的breathe()函數就實現了對animal類中breathe函數的覆蓋。fish類中的breathe()函數仍然是虛函數。

隱藏

例一
class animal
{
public:
    ...
    void breathe()
    {
        cout << "animal breathe" << endl;
    }
    ...
};

class fish:public animal
{
public:
    ...
    void breathe()
    {
        cout << "fish bubble" << endl;
    }
    ...
};

與覆蓋中的代碼相比,此段代碼中,派生類fish和基類animal中的breathe函數也是完全一樣的。不同的是breathe函數不是虛函數,這種情況稱為函數的隱藏。所謂隱藏,是指派生類中具有與基類同名的函數(不考慮參數列表是否相同),從而在派生類中隱藏了基類的同名函數。

兩種函數隱藏的情況:

派生類的函數與基類的函數完全相同(函數名和參數列表都相同),只是基類的函數沒有使用virtual關鍵字。此時基類的函數將被隱藏,而不是覆蓋。 派生類的函數與基類的函數同名,但參數列表不同,在這種情況下,不管基類的函數聲明是否有virtual關鍵字,基類的函數都將被隱藏(覆蓋的條件是函數名和參數列表都相同,且基類中函數用virtual關鍵字修飾)。

例二


class Base
{
public:
    virtual void fn();
};

class Derived:public Base
{
public:
    void fn(int);
};

class Derived2:public Derived
{
public:
    void fn();
};

在Derived類中的fn()函數隱藏了Base類中的fn()函數,因此Derived類中的fn()函數不是虛函數。因為兩個函數的參數列表不同。 在Derived2類中的fn()函數覆蓋類Derived類中的fn()函數。因為Derived2類中的fn()函數和Base類中的fn()虛函數具有相同的函數名和參數列表,因此Derived2類中的fn()函數是虛函數。注意:在Derived2中,Base類的fn()函數是不可見的,但這並不影響fn函數的覆蓋,因為Derived2也是Base類的派生類。 當隱藏發生時,如果在派生類的同名函數中想要調用基類的被隱藏函數,可以使用類名::函數名(參數)的語法形式。

10.引用

引用就是一個變量的別名。它需要用另一個變量或對象來初始化自身。引用就像一個人的外號一樣。

例一
//下面的代碼生命了一個引用b,並用變量a進行了初始化
int a = 5;
int &b = a;

用&表示申明一個引用,引用必須在申明時進行初始化。


int a = 5;
int& b = a ;
int c = 3;
b = c;

上例中,並不是將b變成c的引用,而是給b賦值,此時b和a的值都變成了3。

例二
#include 

using namespace std;

//change函數主要用來交換a和b的值
void change(int& a, int& b);

int main(void)
{
    int x = 5;
    int y = 3;
    cout << "original x = " << x << endl;
    cout << "original y = " << y << endl;
    change(x, y); //此處如果用指針傳遞,則調用change(&x, &y),這樣很容易讓人迷惑,不知道交換的是x和y的值,還是x和y的地址?此處使用引用,可讀性就比指針要好
    cout << "changed x = " << x << endl;
    cout << "changed y = " << y << endl;
    return 0;
}
/*
change()函數中采用了一個巧妙的算法來實現了a和b值的互換
*/
void change(int& a, int& b)
{
    a = a+b;
    b = a-b;
    a = a-b;
}

上述例子中,不能將函數定義成void change(int a, int b)。 如果定義成這樣,在調用完成之後,x還是等於原來的x,y還是等於原來的y,因為函數中的a=x,b=y,而x!=a,y!=b。使用引用就不同了,使用引用後,a和x,b和y事實上是相同的,因為他們在內存中占用的是同一個內存單元。


11.C++類的設計習慣及頭文件重復包含問題的解決


在設計一個類的時候,通常是將類的定義及類成員函數的聲明放到頭文件(即.h文件)中,將類中成員函數的實現放到源文件(即.cpp)中。對於main()函數,我們則單獨把它放到main.cpp文件中。

對於頭文件重復包含的情況,如main.cpp包含了animal.h文件和fish.h文件,而fish.h文件又包含了animal.h文件。這樣就會出現頭文件重復包含了,編譯報錯:‘class’ type redefinition。解決方法如下,在每一個頭文件中,都使用條件預處理指令,如下:


#ifndef _ANIMAL_H_
#define _ANIMAL_H_
class animal
{
public:
    animal();
    ~animal();
    void eat();
    void sleep();
    virtual void breathe();
};
#endif

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved