程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++之父力作學習筆記(4)——類的好多事

C++之父力作學習筆記(4)——類的好多事

編輯:C++入門知識

 

   類,這個概念比較大。包含的事太多。咱們就一一的盡量弄清楚它。

      一個類就是一個用戶定義類型。C++裡類概念的目標就是為程序員提供一種建立新類型的工具,是這些新類型的使用能夠像內部一樣方便。

      訪問控制:class成員的默認訪問方式是私有的。一個struct也是一個class,但是其成員的默認方式是公用的。非成員函數禁止訪問私有成員。

      構造函數:就是函數名和類名一樣的函數且沒有返回值。這誰都知道。It's easy。而默認構造函數就是調用時不必提供參數的構造函數。如果用戶自己聲明了一個默認構造函數,那麼就會去使用它;否則,如果有必要,而且用戶沒有聲明其他的構造函數,編譯器就會設法去生成一個。編譯器生成的默認構造函數將隱式地為類類型的成員和它的基類調用有關的默認構造函數。這裡解釋一下:類類型(Class type)即指那些由程序員定義的類而產生的類型,以便與內部類型和其他用戶定義類型相區分。相信大家這裡也沒什麼問題。有一個注意點來了,由於const和引用必須進行初始化,包含const或引用成員的類就不能進行默認構造,除非程序員的我們自己顯示的提供默認構造函數。例如:struct X

{

    const int a;

    const int& r;

};

X x;//錯誤;X無默認構造函數      默認構造函數也可以顯示調用。內部類型同樣也有默認構造函數。

      下面再談談復制構造函數,先看看復制構造函數是怎麼引進來的。

按照默認約定,類對象可以復制。特別是可以用一個類的對象和復制對該類的其他對象進行初始化。即使是聲明了構造函數的地方,也是可以這樣做:Date d=today;//通過復制初始化按照默認方式,類對象的復制就是其中各個成員的復制。如果某個類X所需要的不是這種默認方式,那麼就可以定義一個復制構造函數X::X(const X&),由它提供所需要的行為。還有一個概念就是復制賦值,很容易和復制構造函數搞混。咱們就一起搞清楚它們。先看一段程序:void h()

{

    Table t1;

    Table t2=t1;//復制初始化

    Table t3;

    t3=t2;      //復制賦值

}看似好像沒什麼問題,對於復制上面提到的解釋方式,在應用到具有指針成員的類的對象時,就可能產生一種出人意料的作用。對於包含了由構造函數/析構函數管理的資源的對象而言,按成員復制的語義通常是不正確的。在這裡,Table的默認構造函數為t1和t3各調用了一次,一共是兩次。然而Table的析構函數則被調用了三次;對t1、t2和t3各一次!由於賦值的默認解釋是按成員賦值,所以在h()結束時,t1、t2和t3中將各包含一個指針,它們都指向建立t1時從自由存儲中分配的那個名字數組。在建立t3時所分配的數組的指針並沒有保留下來,因為它被賦值t3=t2覆蓋掉了。這樣,如果沒有自動廢料收集,對這個程序而言,該數組的存儲就將永遠丟掉了。而在另一方面,為t1的創建而分配的數組因為同時出現在t1、t2和t3裡,將被刪除3次。這種情況所導致的結果是無定義,很可能是災難性的。這類反常情況可以避免,方式就是將Table復制的意義定義清楚:class Table

{

    //---

    Table(const Table&);//復制構造函數

    Table& operator=(const Table&);//復制賦值

};咱們自己可以為這些復制操作定義自己認為最合適的任何意義,例如

//這裡補上Table類的詳細定義

class Table

{

    Name* p;

    size_t sz;

public:

    Table(size_t s=15)

    {

        p=new Name[sz=s];

    }

    ~Table()

    {

        delete[] p;

    }

    Name* loopup(const char*);

    bool insert(Name*);

}

 

Table::Table(const Table& t)//復制構造函數

{

   p=new Name[z=t.sz];

   for(int i=0;i<sz;i++)

      p[i]=t.p[i];

}

 

Table& Table::operator=(const Table& t)//賦值

{

    if(this!=&t)//當心自賦值:t=t

    {

        delete[] p;

        p=new Name[sz=t.sz];

        for(int i=0;i<sz;i++)

            p[i]=t.p[i];

    }

    return *this;

}情況幾乎總是如此,復制構造函數與復制賦值通常都很不一樣。究其根本原因,復制構造函數是去完成對為初始化的存儲區的初始化,而復制賦值運算符則必須正確處理一個結構良好的對象。

      成員常量:

      對那些靜態整型成員,可以給它的成員聲明加上一個常量表達式作為初始式,例如

class Curious

{

    static const int c1=7;//ok,但要記得去定義

    static int c2=11;//錯誤:非const

    const int c3;//錯誤:非Static

    static const int c4=f(1);//錯誤:在類裡的初始表達式不是常量

    static const float c5=7.0;//錯誤:在類裡初始化的不是整型

}1)在類中不能用const來創建常量!因為:類只是描述了對象的形式,並沒有真正創建對象!所以, 在對象建立之前,並沒有存值空間!

2)而const是用來創建常量的! 方法1 你可以用枚舉:

class a

{

enum{buf_size_t buf_size=、、、}//用枚舉創建一個常量,但不是數據成員

 

}

方法2 你可以用static

class a

{

private:

  static const buf_size_t buf_size=30;//該常量將與憋得靜態常量存儲在一起,而不是存儲在對象中

}但《C++程序設計語言》書上說當你用到某個被初始化的成員,而且需要將它作為對象存入存儲器時,這個成員就必須在某處有定義。初始式不必重復寫:const int Curious::c1;//必須,但這裡不必重復初始式

const int* p=&Cusious::c1;//ok:Curious::c1已經有定義這裡有點讓我懵了,為什麼還要const int Curious::c1;//必須,但這裡不必重復初始式 這一行呢?還說是必須,經過測試是有問題的——當前范圍內的定義或重新聲明非法,到底是書錯了還是還有其他什麼原因?

還請高手不吝賜教

今天就到這裡吧,到這裡就出了問題,還需要思考。

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