溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

類與對(duì)象的認(rèn)識(shí)

發(fā)布時(shí)間:2020-08-02 19:07:05 來源:網(wǎng)絡(luò) 閱讀:280 作者:be_better_ 欄目:編程語言

類和對(duì)象

c是一門面向過程的語言關(guān)注的過程,c++作為一門面向?qū)ο蟮恼Z言關(guān)注的時(shí)對(duì)象,把事情拆分為不同的對(duì)象,通過對(duì)象的交互來實(shí)現(xiàn)。

一.類和對(duì)象的初認(rèn)識(shí)

1.類的引入
類用關(guān)鍵字class引出,與之前的結(jié)構(gòu)體類似,不同的是結(jié)構(gòu)體內(nèi)不能定義函數(shù)而在類中可以定義函數(shù)啦(c++中結(jié)構(gòu)體也可以定義函數(shù)哦)
2.類的定義
類中成員函數(shù)的兩種定義方式:可以在類中定義定義,但編譯器有可能將其當(dāng)作內(nèi)聯(lián)函數(shù)來處理故推薦第二種——將函數(shù)的聲明和定義分開。
3.類的訪問限定符
public、protected、private--->protected和private修飾的對(duì)象在類外不能直接訪問
class類默認(rèn)是private,struct默認(rèn)是public
面向?qū)ο蟮娜筇匦裕悍庋b、繼承、多態(tài),這里我們說一下封裝的定義:將數(shù)據(jù)和操作數(shù)據(jù)的方法進(jìn)行有機(jī)結(jié)合,隱藏對(duì)象的屬性和實(shí)現(xiàn)細(xì)節(jié),僅對(duì)外公開接口來和對(duì)象進(jìn)行交互。
4.類的作用域
類定義了一個(gè)新的作用域,若要訪問類中的成員要用作用域限定符::進(jìn)行訪問。
在這里我們已經(jīng)接觸了四個(gè)作用域:全局作用域、函數(shù)內(nèi)的局部作用域、類、命名空間。
5.類的實(shí)例化(對(duì)象)
類限定了有那些成員,類是沒有空間開辟的,用類創(chuàng)建出的對(duì)象才有實(shí)際的內(nèi)存空間。
到這里我們要思考一個(gè)問題,一個(gè)類的大小該如何計(jì)算呢??類中可以包含成員函數(shù),此時(shí)又該怎么辦呢??
通過測(cè)試我們可以知道類的大小其實(shí)就是類的成員變量的大小,類中的成員函數(shù)被放在公共代碼區(qū),要注意內(nèi)存對(duì)齊的問題。如果是一個(gè)空類,或這個(gè)類中只有成員函數(shù),呢么這個(gè)類的大小是1,因?yàn)閯?chuàng)建對(duì)象時(shí)要有區(qū)分。
6.this指針
c++對(duì)每個(gè)成員函數(shù)增加了一個(gè)默認(rèn)的指針參數(shù),讓該指針指向當(dāng)前對(duì)象(函數(shù)運(yùn)行時(shí)調(diào)用該函數(shù)的對(duì)象),在函數(shù)體中所有成員變量的操作,都是通過該指針去訪問。只不過所有的操作對(duì)用戶是透明的,即用戶不需要來傳遞,編譯器自動(dòng)完成。呢么這個(gè)就相當(dāng)于我們用戶自己在傳參時(shí)傳入地址,正因?yàn)橛衪his指針的存在所以函數(shù)才能準(zhǔn)確的訪問到調(diào)用它的主體。

  • this指針的特性
  • this指針的類型:類類型 * const,故this指針的指向是不能修改的。
  • this指針作為函數(shù)的形參,函數(shù)調(diào)用時(shí)將對(duì)象的地址傳給this形參,故對(duì)象中不存儲(chǔ)this指針

    二.類中的默認(rèn)函數(shù)之構(gòu)造函數(shù)

    一個(gè)類定義好的話里面并不是什么都沒有,會(huì)自動(dòng)生成六個(gè)默認(rèn)函數(shù)
    1.構(gòu)造函數(shù)
    構(gòu)造函數(shù)是干什么的呢?
    構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類名相同,創(chuàng)建類類型對(duì)象時(shí)由編譯器自動(dòng)調(diào)用,保證每個(gè)數(shù)據(jù)成員都有 一個(gè)合適的初始值,并且在對(duì)象的生命周期內(nèi)只調(diào)用一次。也就是說構(gòu)造函數(shù)完成了對(duì)象的初始化,它的特點(diǎn)是:函數(shù)名與類名相同、無返回值、對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)、構(gòu)造函數(shù)可以重載。系統(tǒng)自動(dòng)生成的是無參的構(gòu)造函數(shù),當(dāng)然我們可以自己寫一個(gè)帶有參數(shù)的構(gòu)造函數(shù),那么我們?cè)诙x對(duì)象的時(shí)候就可以直接初始化拉,看下面的代碼:
    class data<br/>{<br/>public:<br/>data(int year=1999,int month=1,int day=1)<br/>{<br/>_year = year;<br/>_month = month;<br/>_day = day;<br/>}<br/>private:<br/>int _year;<br/>int _month;<br/>int _day;<br/>};

那么在這個(gè)地方我們就完成了構(gòu)造函數(shù)的創(chuàng)建,呢么在主函數(shù)內(nèi)定義對(duì)象的時(shí)候就可以直接初始化對(duì)象:
data a (1999,10,13);

那么我們知道系統(tǒng)給的是無參的構(gòu)造函數(shù),所以我們?cè)诙x無參構(gòu)造函數(shù)的時(shí)候就不需要帶(),否則就變成了函數(shù)聲明。
上述的代碼就是我們顯式創(chuàng)建的構(gòu)造函數(shù),當(dāng)有它存在的時(shí)候編譯器就不會(huì)再生成默認(rèn)構(gòu)造函數(shù)

還有一個(gè)注意的點(diǎn):無參構(gòu)造函數(shù)和全缺省構(gòu)造函數(shù)都稱作默認(rèn)函數(shù),是不允許重復(fù)定義的,只允許存在一個(gè),故這兩個(gè)是不能構(gòu)成重載函數(shù)的。

呢么構(gòu)造函數(shù)的作用是什么呢?-->因?yàn)樽兞款愋筒恢皇枪潭ǖ膇nt或char這種,也有可能是用戶自定義類型,那么構(gòu)造函數(shù)就會(huì)調(diào)用這種自定義類型的成員函數(shù),這就是它的作用。

2。構(gòu)造函數(shù)賦初值
上述過程中在構(gòu)造函數(shù)體內(nèi)的操作并不是初始化,那么我們習(xí)慣將用構(gòu)造函數(shù)的初始化列表對(duì)其進(jìn)行初始化。

初始化列表:以一個(gè)冒號(hào)開始,接著是一個(gè)以逗號(hào)分隔的數(shù)據(jù)成員列表,每個(gè)"成員變量"后面跟一個(gè)放在括號(hào)中的初始值或表達(dá)式。
public:
data(int year=1999,int month=1,int day=1)
:_year(year)
, _month(month)
, _day(day)
{

}

注意:1.就算沒有初始化列表系統(tǒng)也會(huì)自動(dòng)生成一個(gè)初始化列表,每個(gè)變量只能初始化一次,而且當(dāng)成員變量是引用類型、const修飾、類類型成員變量(沒有默認(rèn)構(gòu)造函數(shù))這三種情況時(shí)必須使用初始化列表對(duì)其初始化,當(dāng)一個(gè)沒有默認(rèn)構(gòu)造函數(shù)的類類型成員變量不初始化系統(tǒng)也不會(huì)自動(dòng)識(shí)別該如何初始化,故會(huì)出錯(cuò)。

2.成員變量的初始化順序是按照成員變量的聲明順序決定的,與初始化列表的順序無關(guān),那么我們?cè)诔跏蓟斜肀M量于聲明的順序相同,更不要用變量進(jìn)行賦值,極容易出錯(cuò)。

  1. explicit關(guān)鍵字
    在c++中發(fā)現(xiàn)了一個(gè)有趣的現(xiàn)象,data a = 100; 這條語句居然可以通過編譯,等號(hào)兩邊的操作數(shù)類型不同卻可以通過編譯,于是我們可以研究一下這個(gè)復(fù)制重載在調(diào)用時(shí)的過程,進(jìn)過測(cè)試我們發(fā)現(xiàn)在進(jìn)行賦值運(yùn)算符重載時(shí)首先調(diào)用了一次構(gòu)造函數(shù),為100構(gòu)造出一個(gè)類!所以構(gòu)造函數(shù)不光有構(gòu)造的作用,對(duì)于單個(gè)參數(shù)(實(shí)參)的構(gòu)造函數(shù)還有類型轉(zhuǎn)化的作用。
    但有時(shí)我們不希望有這種情況的發(fā)生,用explicit修飾在構(gòu)造函數(shù)前就會(huì)禁止這種類型轉(zhuǎn)化。


3.static成員

在c++中static可以修飾成員變量和成員函數(shù),由static修飾的變量或函數(shù)稱作靜態(tài)成員變量或靜態(tài)成員函數(shù)
static修飾的為所有類對(duì)象共享的!

區(qū)別:
| 靜態(tài)成員變量 | 普通成員變量 |
| 初始化在類外 | 類內(nèi)初始化 |
| 所有對(duì)象共享的 | 每個(gè)對(duì)象都包含了一份 |
| 也可以通過類名::靜態(tài)成員名訪問 | 只能通過對(duì)象訪問 |
| 所有對(duì)象共享的 | 每個(gè)對(duì)象都包含了一份 |

靜態(tài)成員變量不會(huì)影響類的大小,也就是計(jì)算類的大小時(shí)不需要計(jì)算靜態(tài)成員的大小,他們用的是同一塊內(nèi)存空間

| 靜態(tài)成員函數(shù) | 普通成員函數(shù) |
| 沒有隱藏的this指針 (不能訪問非靜態(tài)成員) | 有this指針 |
| 不能被const修飾 (因?yàn)閏onst修飾成員函數(shù)實(shí)際上修飾的時(shí)this指針) | 可以 |
| 可以不通過對(duì)象調(diào)用 | 必須通過對(duì)象調(diào)用 |

用static修飾的靜態(tài)成員函數(shù)只能訪問靜態(tài)成員變量,不能訪問普通成員變量。
而普通成員函數(shù)都可以訪問。

二.析構(gòu)函數(shù)

析構(gòu)函數(shù)與構(gòu)造函數(shù)函數(shù)相對(duì)應(yīng),和構(gòu)造函數(shù)構(gòu)成函數(shù)重載,也就是說函數(shù)名也是類名,在函數(shù)名前加~代表析構(gòu)函數(shù),這個(gè)函數(shù)沒有參數(shù)和返回值,析構(gòu)函數(shù)并不是對(duì)類對(duì)象進(jìn)行銷毀,而是完成類的一些資源清理工作。對(duì)象在生命周期結(jié)束時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)。

三.拷貝構(gòu)造函數(shù)

拷貝構(gòu)造函數(shù):只有單個(gè)形參,該形參是對(duì)本類類型對(duì)象的引用(一般常用const修飾),在用已存在的類類型對(duì)象創(chuàng)建新對(duì)象時(shí)由編譯器自動(dòng)調(diào)用。

注意:拷貝函數(shù)是對(duì)構(gòu)造函數(shù)的一個(gè)重載,它的形參只有一個(gè)且必須船引用,如果傳值調(diào)用會(huì)無窮遞歸。

那么有這個(gè)函數(shù)的存在我們?cè)诙x對(duì)象時(shí)就可以用已有的對(duì)象進(jìn)行新對(duì)象的定義:
如:date d1; data d2(d1);

同樣的,編譯器也會(huì)為我們生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù),但它完成的時(shí)淺拷貝工作,比如下面的情況:

class String
{
public:
    String(const char* str = "jack")
    {
        _str = (char*)malloc(strlen(str) + 1);
        strcpy(_str, str);
    }

    ~String()
    {
        cout << "~String()" << endl;
        free(_str);
    }
private:
    char* _str;
};

int main()
{
    String s1("hello");
    String s2(s1);
}

在構(gòu)造函數(shù)內(nèi)我們完成了動(dòng)態(tài)創(chuàng)建內(nèi)存空間,這種情況默認(rèn)拷貝構(gòu)造函數(shù)就會(huì)原封不動(dòng)的把這塊地址拷貝到新對(duì)象d2中,這并不是我們想要的結(jié)果,d2和d1共用同一塊內(nèi)存空間對(duì)d2進(jìn)行修改d1也會(huì)改變。這叫做淺拷貝,所以我們有必要自定義一種深拷貝的構(gòu)造函數(shù)。這個(gè)后期在做介紹。

四.賦值運(yùn)算符重載

在談賦值運(yùn)算符重載前我們有必要先談一下什么是運(yùn)算符重載,那么兩個(gè)類是無法比較大小的,但是如果我們進(jìn)行運(yùn)算符重載,按照大小關(guān)系進(jìn)行重載那么類也就可以使用比較運(yùn)算符了,其它運(yùn)算符也是一樣的。

用關(guān)鍵字operator進(jìn)行運(yùn)算符重載
有一類特殊的運(yùn)算符重載,就是前置++和后置++的重載,為了有所區(qū)別

賦值運(yùn)算符主要有四點(diǎn):

  1. 參數(shù)類型
  2. 返回值
  3. 檢測(cè)是否自己給自己賦值
  4. 返回this//this作為返回值是為了解決連續(xù)賦值的問題
  5. 一個(gè)類如果沒有顯式定義賦值運(yùn)算符重載,編譯器也會(huì)生成一個(gè),完成對(duì)象按字節(jié)序的值拷貝。

同樣完成的也是淺拷貝,所以我們也有必要自己實(shí)現(xiàn)一個(gè)賦值運(yùn)算符的重載。深拷貝的實(shí)現(xiàn)同樣也放在后面說。

最后兩種默認(rèn)函數(shù)是普通變量和const變量的取地址,這兩種很少自己實(shí)現(xiàn)。

四.友元

如果我們對(duì)輸出運(yùn)算符“<<”進(jìn)行重載該怎么做呢
ostream & operator<<(ostream & _cout)------>這是對(duì)<<重載的函數(shù)函數(shù),如果這樣定義函數(shù)的話其中包含的this指針作為第一個(gè)形參,所以在調(diào)用函數(shù)想要輸出一個(gè)類的時(shí)候就不能按照常規(guī)的cout<<d這種方式進(jìn)行輸出,函數(shù)的第一個(gè)參數(shù)是this指針,所以調(diào)用時(shí)要d<<cout,這樣才能正常的輸出。
那么我們?cè)趺茨鼙苊膺@種方式而是按照常規(guī)的方式對(duì)<<進(jìn)行重載呢

這里就要我們就要在全局作用域定義一個(gè)重載函數(shù)把兩個(gè)參數(shù)按照正常的順序傳入,但是在類中就不能不能引用這個(gè)函數(shù)的私有成員,我們只要在類中聲明這個(gè)函數(shù)用friend關(guān)鍵字修飾那么這個(gè)函數(shù)就是這個(gè)類的友元函數(shù)也就可以在類內(nèi)訪問了

注意:
友元函數(shù)是普通函數(shù)并不是類中的成員函數(shù)
友元函數(shù)可訪問類的私有成員,但不是類的成員函數(shù)
友元函數(shù)不能用const修飾(因?yàn)閏onst修飾的是this指針,而友元函數(shù)并沒有this指針)
友元函數(shù)可以在類定義的任何地方聲明,不受類訪問限定符限制
一個(gè)函數(shù)可以是多個(gè)類的友元函數(shù)
友元函數(shù)的調(diào)用與普通函數(shù)的調(diào)用和原理相同

同樣一個(gè)類也可以是另一個(gè)類的友元類,這個(gè)類就可以訪問其內(nèi)部的私有成員了,但注意此過程是單向的且不能傳遞。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

c++
AI