從零開始學C++之類與對象:類聲明、類範圍、前向聲明、this指標、嵌套類、PIMPL 技法 等

來源:互聯網
上載者:User

一、類聲明

//類是一種使用者自訂類型,聲明形式:
class 類名稱
{
   public:
             公有成員(外部介面)
   private:
             私人成員
   protected:
             保護成員
};

在關鍵字public後面聲明,它們是類與外部的介面,任何外部函數都可以訪問公有類型資料和函數。
在關鍵字private後面聲明,只允許本類中的函數訪問,而類外部的任何函數都不能訪問。
在關鍵字protected後面聲明,與private類似,其差別表現在繼承與派生時對衍生類別的影響不同。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
class Clock
{
public:
    void Display();
    void Init(int hour, int minute, int second);

private:
    int hour_;
    int minute_;
    int second_;
};


假設定義了一個Clock 類,因為成員是private的,那麼 Clock ck;  ck.hour_ = 12; 是錯誤的,對此定義一個public 的void SetHour(int hour) 來設定hour_ 的值。


二、內聯成員函數、成員函數的重載及其預設參數

在這裡有內嵌函式的概念。成員函數也可以是內聯的,若在類內部實現,inline 關鍵字可加可不加;在類外部實現,需加inline,

如 inline void Clock::SetHour(int hour) { } 。實際上即使加了inline也不一定宏展開,比如遇到switch,for 語句的時候就往往不會。

此外,成員函數也像一般函數那樣可以重載,也可以有預設參數,參考這裡。


三、類與結構體

class與struct的區別:在未指定存取權限時,class預設的是私人的,struct預設是公有的,

struct Test
{
    int X;//公有的
     ...
};

此外,Test 可以獨立作為一個tag,而不像C語言那樣需要 struct Test 作為一個類型。


四、隱含的 this 指標

成員函數有一個隱含的附加形參,即指向該對象的指標,這個隱含的形參叫做this指標(編譯器自動傳遞)
使用this指標保證了每個對象可以擁有不同數值的資料成員,但處理這些成員的代碼可以被所有對象共用

成員函數是唯讀代碼,由所有對象共用,並不佔對象的儲存空間,因為this指標指向當前對象,所以成員函數可以區分它所作用的對象是哪一個。


五、類範圍、前向聲明

(1)、每個類都定義了自己的範圍稱為類範圍,類範圍中說明的標識符只在類中可見。除了類範圍,還有塊範圍、檔案範圍、函數原型範圍、函數範圍,舉個例子:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
using namespace std;

class Test
{
public:
    int num_;
};

//num_ = 20;        Error,num_的範圍在類內部
int num_ = 20;      // num_的範圍是檔案範圍,與類中的num_是不同的範圍

int add(int a, int b);  // a, b兩個標識符的範圍為函數原型範圍

int main(void)
{
    int num_ = 30;      // num_為塊作域
    {
        int num_ = 100; // num_為塊作域
    }

    cout << num_ << endl;
    cout <<::num_ << endl;
    return 0;
}

int add(int a, int b)   // 形參a與b也算是塊範圍
{
    return a + b;
}

int test()
{
LABEL1: //函數範圍
    cout << "label1" << endl;
    goto LABEL3;
LABEL2:
    cout << "label2" << endl;
    goto LABEL1;
LABEL3:
    cout << "label3" << endl;
    goto LABEL2;
}

(2)、C++中類必須先定義,才能夠執行個體化。兩個類需要相互引用標頭檔形成一個“環形”引用時會出錯。這時候需要用到前向聲明,前向聲明的類不能執行個體,但可以定義指標或引用。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef _B_H_
#define _B_H_

class A;

class B
{
public:
    B(void);
    ~B(void);

    void fun(A &a)
    {

    }

    A *a_;      // 前向聲明的類不能執行個體化對象
};

#endif // _B_H_

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef _A_H_
#define _A_H_

#include "B.h"
class A
{
public:
    A(void);
    ~A(void);

    B b_;
};

#endif // _A_H_

六、嵌套類、局部類

(1)、嵌套類

外圍類需要使用嵌套類對象作為底層實現,並且該嵌套類只用於外圍類的實現,且同時可以對使用者隱藏該底層實現。

從範圍的角度看,嵌套類被隱藏在外圍類之中,該類名只能在外圍類中使用。如果在外圍類之外的範圍使用該類名時,需要加名字限定。

嵌套類中的成員函數可以在它的類體外定義。

嵌套類的成員函數對外圍類的私人成員沒有訪問權,反之亦然。

嵌套類僅僅只是文法上的嵌入

(2)、局部類

類也可以定義在函數體內,這樣的類被稱為局部類(loacl class)。局部類只在定義它的局部域內可見。

局部類的成員函數必須被定義在類體中。

局部類中不能有靜態成員,關於類中的靜態成員和靜態成員函數以後再談。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
using namespace std;

class Outer
{
public:
    class Inner
    {
    public:
        void Fun();
        //{
        //  cout<<"Inner::Fun ..."<<endl;
        //}
    };
public:
    Inner obj_;
    void Fun()
    {
        cout << "Outer::Fun ..." << endl;
        obj_.Fun();
    }
};

void Outer::Inner::Fun()
{
    cout << "Inner::Fun ..." << endl;
}

void Fun()
{
    class LocalClass
    {
    public:
        int num_;
        void Init(int num)
        {
            num_ = num;
        }
        void Display()
        {
            cout << "num=" << num_ << endl;
        }

        //static int num2_; // 局部類內部不能定義靜態成員
    };

    LocalClass lc;
    lc.Init(10);
    lc.Display();
}

int main(void)
{
    Outer o;
    o.Fun();

    Outer::Inner i;
    i.Fun();

    Fun();
    //LocalClass lc;        Error,局部類只能在定義它的函數體中使用
    return 0;
}


七、PIMPL 技法


來看下面的樣本:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// file y.h
#include "x.h“
class Y
{
    void Fun();
    X x_;};
// file y.cpp
#include “y.h”
void Y::Fun
{
    return x_.Fun();
}
// file main.cpp
#include “y.h”
int main(void)
{
    Y y;
    y.Fun();
}

上面程式存在的問題是:

1、引入更多的標頭檔,降低編譯速度

2、在編譯期如果X的大小改變了,y.cpp 和 main.cpp 都得重新編譯;在運行期,如果X有子類,也不能使用多態虛函數。

3、假設y.cpp 編譯成動態庫給main.cpp 使用,當X的大小變化,動態庫需要重新編譯,此時main.cpp 因為有定義對象y ,故也需要

重新編譯。


下面介紹一種PIMPL 技法,有人也把它當作一種設計模式:


PIMPL(private implementation或pointer to implementation)也稱為handle/body idiom

PIMPL背後的思想是把客戶與所有關於類的私人部分的知識隔離開。避免其它類知道其內部結構

降低編譯依賴、提高重編譯速度

介面和實現分離

降低模組的耦合度

編譯期

運行期

提高了介面的穩定程度

對於庫的使用,方法不能改變

對於庫的編譯,動態庫的變更,客戶程式不用重新編譯

修改後的程式:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// file y.h
class X;
class Y
{
    Y();
    ~Y();
    void Fun();
    X *px_;
};
// file y.cpp
#include "x.h"
Y::Y() : px_( new X ) {}
Y::~Y()
{
    delete px_;
    px_ = 0;
}
void Y::Fun()
{
    return px_->Fun();
}
//  file main.cpp
#include “y.h”
int main(void)
{
    Y y;
    y.Fun();
}

即Y 內部成員是X* 指標,在32位系統上,指標大小固定為4個位元組,即使X大小改變,也不影響Y。如果X 有子類,通過基類指標px_ 

還可以實現虛函數多態。

參考:

C++ primer 第四版
Effective C++ 3rd
C++編程規範


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.