C到C++ 快速過度 C 結構體到類

jopen 9年前發布 | 13K 次閱讀 C/C++開發 C/C++

“類”這個概念對C++來說意義非凡,與面向對象密不可分。 從這個概念開始,C++比C復雜的一面越加凸顯。

還記得C語言中的結構體么?
struct Point{
    double x;
    double y;
};
上面的代碼是一個“結構體模版”,我們利用它創建了一個叫做Point的類型。

在這之后,我們就可以像聲明基本類型的變量一樣聲明Point類型:
Point ob;

ob叫做結構體Point的一個“實例”。

而當我們 int n; 的時候,會講n是int類型的一個“變量”。



結構體是一種復合類型,但是它和同樣身為復合類型的數組不同:


<!--數組操作技巧-->

char name[20];
我們這樣就聲明了一個數組,并且每個name[i](0 <=i < 20)都是一個獨立的char類型的變量。它們被稱為數組的“元素”。

值得一提的是,同一個數組的各個元素所占用的內存空間是連續的。

這使得我們在遍歷(檢索)整個數組的時候,速度非常的快:

int line[] = {2, 5, 6, 7, 8, 12, -5, -32};        // 1
int len = sizeof(line) / sizeof(int);            // 2
for(int i = 0; i < len; i++)
    line[i] = -line[i];

上述語句遍歷這個數組,并且將每個元素的值都取反。

第 1 個語句說明,數組可以根據你聲明時填寫的數據自動分配大小,但是一旦分配,數組的大小就定了。

第 2 個語句可以直接確定數組中元素的個數,這樣就無需一個個數了。
sizeof是一個操作符,用法類似于函數。當參數是數組名時,將得到整個數組所占內存的字節數;而當參數是類型時,得到的是該類型變量的字節數。

而對于字符串數組,初始化有更方便的方法:

char name[] = "Always Laugh";

這樣name就會被自動分配成長度13的數組(別忘了'\0')。


除了之前介紹的使用sizeof操作符取元素個數的方法以外,頭文件<string.h>(C++中shi<cstring>)中的strlen函數可以更方便地取字符串長度:

char name[] = "Always Laugh";
int len = strlen(name);
for(int i = 0; i < len; i++)
    name[i]++;

這個代碼會把name數組的的每個元素在字符表中“后移”一位。

需要注意的是,strlen只能處理char的字符串數組,對int等不適用。

并且它以字符串末尾的'\0'字符為結束標志,因此取到的往往不是數組大小。



<!--類-->

現在介紹C++中的“類”這個概念,它和結構體極其相似。

我們先寫一個使用結構體的程序:

#include <iostream>

using namespace std;

struct Point{                //  1 結構體的模版

    double x;        //  成員

    double y;

};

int main()

{

    Point p;                //  2 結構體的聲明

    p.x = 3.2;                 //  3 結構體的使用

    p.y = 8.9;

    cout << "(" << p.x << "," << p.y << ")" << endl;    // 結構體的使用

    return 0;

}

結構體的模版和函數的定義性質相同(之后介紹的“類”也同樣),只是規定了這個結構體/類的內部結構和工作原理,并不分配內存。

我們在使用結構體對象的時候,總是使用它的成員。“.”這個操作符用來連接結構體的實例和它的成員。

實例的名稱可以自由去定義,就像變量名一樣,但是相同類型的實例總是擁有的成員確是完全相同的,都和模版中的一致。

即利用相同模版初始化的實例都具有相同的成員。


而當將類引入時,會發生較大的變化:

#include <iostream>

using namespace std;

class Point{

private:

    double x;

    double y;

public:

    setPoint(double m, double n){

        x = m; y = n;

    }


    printPoint(){

        cout << "(" << x << "," << y << ")" << endl;

    }

};


int main()

{

    Point p;

    p.setPoint(3.2, 8.9);

    p.printPoint();

    return 0;

}


這個程序和用結構體的效果是相同的,但是復雜的地方集中在了類的模版定義部分。

出現了很多陌生的東西,private,public,甚至還有函數。


這是因為,對于類而言,不僅變量可以作為成員,函數也可以。


而當你想要在主函數中使用如下語句時:

p.x = 2.2; p.y = 1.1;

你會發現這個不允許的。

原因就在于關鍵字private(私有的),它將x和y成員保護了起來,使它們在“模版區”之外不能出現。

注意是“不能出現”,而不僅是“不能賦值”。這意味著 cout << p.x; 也是不允許的。


關鍵字public(公有的)定義的成員可以暴露給外界,正如主函數中的p.setPoint(3.2, 8.9);和p.printPoint();一樣。


把x和y設置為私有成員是為了數據的安全性:

int main()

{

    Point p;

    p.setPoint(3.2, 8.9);

    p.printPoint();

    return 0;

}

這是剛剛的主函數,我們從中根本看不出p這個類含有哪種或者多少個私有成員,更不知道成員叫做x/y。

它們確實可以在類模版中看到,但是在工程中我們使用的類,大多數是見不到類模版的,他們存放在不同的文件中。

這樣,我們只需要知道類的公有成員函數接受哪些變量,起到哪些作用,學會調用即可。至于具體細節,內部邏輯,我們不需要知道。

這就是“封裝”,C++的三大特性之一。


正如你看到的,私有成員變量可以在成員函數中直接出現,因為它們處于同一個作用域中(類模版的花括號)。

有私有成員函數嗎?當然有。它可以供其他私有成員函數或者公有成員函數去調用。

總之,只有公有成員(可以出現在“模版區”之外)才會和“.”連用。因為一個“實例”的聲明和使用都是在外界的。


簡而言之:

成員包括私有成員和公有成員。

成員分為成員變量和成員函數。



<!--構造函數-->

class Point{

private:

    double x;

    double y;

public:

    Point(){

    }

    

    Point(double m, double n) {

        x = m; y = n;

    }

    

    setPoint(double m, double n){

        x = m; y = n;

    }

    

    printPoint(){

        cout << "(" << x << "," << y << ")" << endl;

    }

};


仍然是那個類,多了兩個比較怪異的函數。沒錯,函數的名稱和類名稱一樣,并且他們不含返回值。

有了它們,我們便可以在主函數中這樣子聲明p。

int main()

{

    Point p(3.2, 8.9);

    p.printPoint();

    return 0;

}

構造函數是在初始化實例的過程中調用的,這樣我們可以在初始化的同時給成員賦值,而setPoint用來改變成員的值。

為什么Point函數聲明了兩次?還記得函數重載么?我們重載了它。

這樣我們既可以在聲明實例的時候初始化它,也可以不這么做。

因為構造函數總是會執行的。Point p;和Point p();絲毫沒有區別。



<!--些許復雜的示例程序-->

#include <iostream>

#include <cstring>

using namespace std;

struct Friend{

    char name[20];

    Friend* nest;


    Friend(char* pn){

        strcpy(name, pn);

        nest = NULL;

    }

};


struct FriendList{

    Friend* head;

    Friend* mov;

    int number;

    bool none;


    FriendList(Friend* pri){

        head = mov = pri;

        number = 1;

        none = 0;

    }

    

    void ReTop(){

        mov = head;

        none = 0;

    }


    void BeFriend(Friend* someone){

        Friend* temp = mov->nest;

        mov->nest = someone;

        someone->nest = temp;

        number++;

    }


    bool Que(Friend *f){

        if(none) {

            cout << "You got no friends.\n\n";

            ReTop();

            return 0;

        }

        if(!mov->nest) none = 1;

        char ch;

        while(getchar() != '\n') continue;

        cout << "Do you wang to make friends with "

            << mov->name << "?" << endl

            << "Y to YES and N to NO" << endl;

        cin >> ch;

        if(ch == 'Y') {

            cout << "You've been friend with " << mov->name << "!\n\n";

            BeFriend(f);

            ReTop();

            return 0;

        }else{

            mov = mov->nest;

            return 1;

        }

    }


    void End()

    {

        Friend* temp;

        while(number--) {

            temp = head->nest;

            delete(head);

            head = temp;

        }

    }

};


int main()

{

    char name[20] = "Always Laugh";

    Friend *Me = new Friend(name);

    FriendList myFriend(Me);

    int T = 4;

    while(T--) {

        cout << "Enter your name: ";

        cin >> name;

        Friend* you = new Friend(name);

        cout << "Hello " << you->name << "!" << endl;

        while(myFriend.Que(you));

    }

    return 0;

}


這個示例程序對剛接觸C++的人來說難度很大,接近一個課設。因為類的內部聯系復雜。

并且還用到了動態內存分配(比如new和delete操作符),和鏈表的內容(這些內容對大家來說是陌生的)。

這是一個交朋友的程序,大家不妨編譯運行一下,看看效果,再盡力把代碼搞懂(為了避免越界訪問,請不要輸入得太古怪)。


來自:http://my.oschina.net/u/1780798/blog/373623

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!