第 14 章 class 的一些基本架構

C++ 程式 中 類別(class) 的架構 除了

尚有一些其他基本架構, 如: 本章將 說明這幾項 class 的功能。

本 章 主 要 內 容 如 下:

回第 13 章
至第 15 章
回 C 程式主目錄


第 14.1 節 隱藏的 this pointer

每一個 物件都有一個隱藏的 pointer 其名為 this。 對於 class 為 CStack 的 物件 stack1 與 stack2 而言, 都有共同的 member functions push(), 其程式碼為

   void CStack::push(char c)
   {
       s[tos++] = c;
   }
事實上, 將上一個 程式碼改為如下的程式亦可:
   void CStack::push(char c)
   {
      this->s[this->tos++] = c;
   }
對於 物件 stack1 而言, 函數的呼叫 stack1.push('('); 於 函數 push() 中的 *thisstack1
對於 物件 stack2 而言, 函數的呼叫 stack2.push('('); 於 函數 push() 中的 *thisstack2

函數 push() 相對應的 C 程式碼(或 將 C++ 程式 編譯為 C 程式)為

   void push(CStack *this, char c)
   {
       this->s[ this->tos++] = c;
   }
其中 將 CStack 視為一 struct 架構, 如
   struct CStack
   {
       char *s;
       int  tos;
   }

如此一來, 對於物件 stack1 ( 為一 struct CStack 資料型態 ) 而言, 函數 stack1.push('(') 的呼叫變為 push(&stack1, '(');
對於 物件 stack2 ( 為一 struct CStack 資料型態 ) 而言, 函數 stack2.push('(') 的呼叫變為 push(&stack2, '(');

如此一來, 就可以解決 共同函數 push() 的問題。

回本章主目錄


第 14.2 節 static data member

class 的一個 static data member 就如同一個全域變數 (global variable), 它並不屬於該 class 的 data member。 一個 private static data member 是專屬於該 class 的一個全域變數, 一個 public static data member 就如同 C 程式中 的一個全域變數。 其定義方式就是在 一般 data member 之前 加上 static 即可。

例 1: static data member 的定義

    class CTest
    {
        public:
            CTest(){}
            ~CTest(){}

            static int constructNum;
            int value;
        private:
            static int destructNum;
    }

說明:

  1. constructNum; 是如同 C 程式中 的一個全域變數。
  2. destructNum; 是專屬於 class CTest 的一個全域變數。
一個 static data member 起始值 的設定 必須在 class 定義及 函數的 implementation 之外, 如例 2。

例 2: static data member 起始值 的設定

    class CTest
    {
        public:
            CTest(){constructNum++;}
            ~CTest(){destructNum++;}

            static int constructNum;
            int value;

            int getDestructorNum(){ return destructNum;}
        private:
            static int destructNum;
    };

    int CTest::constructNum=0; // static data member 起始值 的設定
    int CTest::destructNum=0;  // static data member 起始值 的設定

    main()
    {

    }

一個 public static data member 就如同 C 程式中 的一個全域變數, 其引用方式, 即將 變數之前加上 class 的標示, 如下:

    CTest:: constructNum 

例 3: public static data member 的引用

    class CTest
    {
        public:
            CTest(){constructNum++;}
            ~CTest(){destructNum++;}
            static int constructNum;
            int value;
            int getDestructorNum(){ return destructNum;}
        private:
            static int destructNum;
    };
    int CTest::constructNum=0; // static data member 起始值 的設定
    int CTest::destructNum=0;  // static data member 起始值 的設定
    class CB
    {
        public:
            CB(){}
            ~CB(){}
            void set(){     // public static data member 的引用
                x = CTest::constructNum; 
            }
            int get(){ return x;}
        private:
           int x;
    };

    main()
    {
       CTest t1, t2, *pt;
       CB b;
       b.set();
       cout<< "number of constructed class CTest = ";
       cout<< CTest::constructNum<< endl; // public static data member 的引用
       cout<< "number of destructed class CTest = ";
       cout<< t1.getDestructorNum()<< endl;
       cout<< "b.x = "<< b.get()<< endl;

       pt = new CTest;
       b.set();
       cout<< "number of constructed class CTest = ";
// public static data member 的引用             
       cout<< CTest::constructNum<< endl; 
       cout<< "number of destructed class CTest = ";
       cout<< t1.getDestructorNum()<< endl;
       cout<< "b.x = "<< b.get()<< endl;

       delete pt;
       cout<< "number of constructed class CTest = ";
// public static data member 的引用
       cout<< CTest::constructNum<< endl;
       cout<< "number of destructed class CTest = ";
       cout<< t1.getDestructorNum()<< endl;
    }
說明:
  1. 於 class CB 中亦可引用全域變數 CTest::constructNum, 該變數宣告的位置 在 引用處之前。
  2. 於 main() 中亦可引用全域變數 CTest::constructNum
  3. 該程式的結果為
    number of constructed class CTest = 2
    number of destructed class CTest = 0
    b.x = 2
    number of constructed class CTest = 3
    number of destructed class CTest = 0
    b.x = 3
    number of constructed class CTest = 3
    number of destructed class CTest = 1

回本章主目錄


第 14.3 節 static member function

對於例 3 中 class CTest 的 member function getDestructorNum() 僅牽扯到 static data member destructNum, 欲知 該變數 destructNum 的值, 實在不用牽扯到任何一個 CTest 的 object。 因此, 可以將該 member function getDestructorNum() 加上 static 而形成一個 static member function。

引用 static member function 的方式與時機 跟 引用 static data member 的方式 相同。

讀者可試將例 3 中 於 main() 的 指令 t1.getDestructorNum() 改成 CTest::getDestructorNum()

回本章主目錄


第 14.4 節 friend 的標示

本節將討論一個非 class 的 member function 如何 來引用 class 中 private elements 如同 class 的 member function 一般, 還有, 不同的 class, 又如何去引用 其他 class 的 private elements。

第 14.4.1 節 friend functions

設有一個函數 int f(const A &a) 含有一個引數 a, 其為 class A 的一物件, 又 函數 f 並不是 class A 的一個 memeber function, 若於 函數 f 的定義中, 欲能自由引用 class A 的 private elements 如同 class A 的 member function 一般, 則需要在 class A 中 宣告為一 friend function, (就好像得到 class A 的允許, 可以使用其 公用 與 私用 資料 ), 其 宣告方式如下:

    class A
    {
      public:
         A(int r=0){x=r;}
         friend int f(const A &a);
      private:
         int x;
    }

    int f(const A &a)
    {
         return a.x*a.x;
    }
說明:
  1. 函數int f(const A &a) 的設計, 可以在 class A 的定義之內,或在 class A 的定義之外, 如上述情況。
  2. 函數int f(const A &a) 並非是 class A 的一個 member function, 因此, 於上述情況 函數int f(const A &a) 的定義並不加上 A:: 的標示。
  3. 函數int f(const A &a) 於 class A 的宣告地方, 不限定在 public 或 private 區。

設有一個函數 int h(const A &a, const B &b) 含有兩個引數 a 與 b, 其分別為 class A 與 class B 的物件, 又函數 h 既不是 class A 的 memeber function, 也不是 class B 的 memeber function。 若 函數 h 欲能自由引用 class A 與 class B 的 private elements, 則需要在 class A 與 class B 中 宣告為一 friend function, 其 宣告方式如下例:

例 4: a friend function of 2 different classes。

     #include <iostream.h>

     class B;
     class A
     {
         public:
           A(int r=0){x=r;}
           ~A(){}
           friend int h(const A &a, const B &b);
         private:
           int x;
     };

     class B
     {
         public:
           B(int r=0){y=r;}
           ~B(){}

           friend int h(const A &a, const B &b);
         private:
           int y;
     };

     int h(const A &a, const B &b)
     {
           return a.x*b.y;
     }

     main()
     {
           A a(10);
           B b(20);

           cout<< "h() = "<< h(a, b)<< endl;
     }
說明:
  1. 函數int h(const A &a, const B &b) 同時為 class A 與 class B 的 friend function。
  2. 該程式的執行結果為 h() = 200

回本章主目錄

第 14.4.2 節 friend classes

一個 class A 的定義中, 亦可宣告 class B 為其 friend, 如此一來, class B 中的 member function f(A &a) 若 有 引數 為 class A 的物件, 則 於函數 f() 的定義中就可以直接引用 class A 的 private elements。 這好像是 class A 宣告 class B 為其 好朋友, 因此, class B 就可以分享 class A 的資源。 由例 5 來看如何使用 friend class 的功能。

例 5 : friend class

     #include <iostream.h>

     class A
     {
       public:
            A(int r=0){x=r;}
            ~A(){}

          friend class B;

       private:
            int x;
     };

     class B
     {
       public:
            B(int r=0){y=r;}
            ~B(){}
            int g(A &a){ return a.x+y;}
       private:
            int y;
     };

     main()
     {
         A a(3);
         B b(12);

         cout<< "g(3) = "<< b.g(a)<< endl;
     }
說明:
  1. 於 class A 中宣告 class B 為其 friend。
  2. class B 的 member function g 可以自由引用 class A 的 private elements, 如 a.x
  3. 該程式的執行結果為 g(3) = 15

回本章主目錄


第 14.5 節 operator

int 有 加、減、乘、除 等運算, 其他 資料型態 與 class 也有相對應的運算。 本節將討論 C++ 程式如何將此概念推衍至 class 的運算。

int 的 加、減、乘、除 運算是二元 運算, 亦是 一函數 其定義域為 N ※ N ,其對應域為 N。 因此, int 的 加法運算 就如同 下列函數 add()

   int add(int x, int y){ return x + y;}

對於向量的運算,有加法運算, 常量與向量相乘,向量的內積及 外積等。 一個 2 度空間向量 u = <2, 3> 與 向量 v = <3, 5>, 其和為 u+v = <5, 8>, 又 其內積為 u*v = 2*3+3*5=212*v =< 2*3, 3*5>=<6, 15>

首先, 我們先來定義 class CVector 如下:

    class CVector
   {
    public:
        CVector(int r=0, int s=0){x=r; y=s;}
        ~CVector(){}

    private:
        int x, y;
   }
欲定義函數 add(const CVector &v) 將兩個 向量加起來, 可以將 函數 add(const CVector &v) 定義為 class CVector 的一個 member function 如下:
    class CVector
   {
    public:
        CVector(int r=0, int s=0){x=r; y=s;}
        ~CVector(){}

        CVector add(const CVector &v)
        {
             CVector u;

             u.x = x + v.x;
             u.y = y + v.y;
             return u;
        }    
    private:
        int x, y;
   }
將兩個向量加起來變成第三個向量, 就如下列程式:
    class u, v, w;
    ...

    w = u.add(v);
將函數 add() 改為 operator +, 其定義方式如下:
    class CVector
   {
    public:
        CVector(int r=0, int s=0){x=r; y=s;}
        ~CVector(){}

        CVector operator +(const CVector &v)
        {
             CVector u;

             u.x = x + v.x;
             u.y = y + v.y;
             return u;
        }    
    private:
        int x, y;
   }
將兩個向量加起來變成第三個向量, 就如下列程式:
    class u, v,w;
    ...

    w = u.operator +(v);
C++ 程式語言 結合了 friend 與 operator 功能,提供了簡潔的運算式 ,如下例所示:

例 6: friend operator +

#include <iostream.h>

class CVector
{
    public:
    CVector(int r=0, int s=0){x=r; y=s;}
    ~CVector(){}

    void printOut()
    {   cout<< "x= "<<  x <<", y= "<<  y << endl;
    }
    friend CVector operator +(const CVector &u, const CVector &v);
    private:
        int x, y;
};


CVector operator +(const CVector &u, const CVector &v)
{
	CVector w;

	w.x = u.x + v.x;
	w.y = u.y + v.y;
	return w;
}

main()
{
    CVector a(10, 20), b(20, 30), sum;

    sum = a + b;

    sum.printOut();
}
說明:
  1. operator + 成為 class CVector 的一個具有左結合律的二元運算。
  2. sum = a + b; 就如同一般運算式。
  3. 該程式的執行結果為 x= 30, y=50

一個具有 2 維向量的運算,如 加法運算、 常量與向量相乘、向量的內積 的 class CVector 其定義如例 7:

例 7: friend operators +, *

#include <iostream.h>

class CVector
{
    public:
        CVector(int r=0, int s=0){x=r; y=s;}
        ~CVector(){}

        void printOut()
        { cout<< "x= "<< x << ", y= "<< y << endl;
        }

    friend CVector operator +(const CVector &a, const CVector &b);
    friend CVector operator *(const CVector &a, const int r);
    friend CVector operator *(const int r, const CVector &a);
    friend int operator *(const CVector &a, const CVector &b);

    private:
        int x, y;
};

CVector operator +(const CVector &a, const CVector &b)
{
    CVector c;

    c.x=a.x+b.x;
    c.y=a.y+b.y;
    return c;
}

 CVector operator *(const int r, const CVector &a)
{
    CVector c;

    c.x=a.x*r;
    c.y=a.y*r;
    return c;
}
 CVector operator *(const CVector &a, const int r)
{
    CVector c;

    c.x=a.x*r;
    c.y=a.y*r;
    return c;
}
int operator *(const CVector &a, const CVector &b)
{
    return a.x*b.x+a.y*b.y;
}
main()
{
    CVector a(1, 2), b(2, 3), sum;
	
    sum =((5*a + b*2)*( 3*a))*a;

    sum.printOut();
}
說明:
  1. operator * 可以做 向量的內積運算, 或 長量與向量相乘。
  2. 該程式的執行結果為 x= 123, y= 246
於第 7.6 節中所談的 struct longint, 將之改為 class CLongint, 其運算 為一般 整數的 加減乘除, 該 class CLongint 的宣告如下:
    class CLongint
    {
       public:
           CLongint(int m=101):MAXSIZE(m){ D = new char[MAXSIZE]; n=0;}
           CLongint(int m=101, long k):MAXSIZE(m)
           { D = new char[MAXSIZE]; n = 0; LongToLongint(k); }
           CLongint(char s[]){ StringToLongint(s); }
           ~CLongint(){}

           void SetZero();
           void LongToLongint(long number);
           void StringToLongint(char s[]);
           char * LongintToString();
           int IsPositive();
           int IsNegative();
           int IsZero();
           void PrintLongint();

       friend int operator >(const CLongint &a, const CLongint &b);
       friend int operator <(const CLongint &a, const CLongint &b);
       friend int operator ==(const CLongint &a, const CLongint &b);

       friend CLongint operator +(const CLongint &a, const CLongint &b);
       friend CLongint operator -(const CLongint &a, const CLongint &b);
       friend CLongint operator *(const CLongint &a, const CLongint &b);
       friend CLongint operator /(const CLongint &a, const CLongint &b);
       friend CLongint operator %(const CLongint &a, const CLongint &b);

       private:
           unsigned int n;
           char *D;
           const int MAXSIZE;
    }

class CLongint 的設計則留給讀者去發揮。

回本章主目錄


回第 13 章
至第 15 章
回 C 程式 主目錄