1. 程式人生 > >C++初識:類和物件(2)

C++初識:類和物件(2)

在之前的文章裡,我們出初步瞭解了,什麼是類,類如何定義,類的大小怎麼計算等

一個空類裡面什麼也沒有,但是它並非是什麼也沒有,只要是類,它就有6個預設的成員函式:

1.建構函式

2.解構函式

3.拷貝建構函式

4.賦值操作符過載

5.取地址操作符過載

6.const修飾的取地址操作符過載

類的建構函式:

class Date{
public:
    void Display()
    {
        cout<<_year<<"_"<<_month<<"_"<<_day<<endl;
    }
    void SetDate(int year,int month,int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year; //年
    int _month; //月
    int _day;  //日
};

int main()
{
    Date firstDate,secondDate;
    firstDate.SetDate(2018,8,15);
    secondDate.SetDate(2018,8,14);
    firstDate.Display();
    secondDate.Display();
    return 0;
}

日期的成員的變數是私有的,我們是怎麼初始化這些變數的?

其實這就是建構函式的功勞,它是一種隨著物件建立而被自動呼叫的公有成員函式,有且僅在定義物件時自動執行一次,它的主要用途是用來作物件的初始化

建構函式是特殊的成員函式,特徵有:

1.函式名與類名相同

2.無返回值

3.物件構造時系統自動呼叫對應的建構函式

4.建構函式可以過載

5.建構函式可以在類中定義,也可以在類外定義

6.如果類定義中沒有給出建構函式,則C++編譯器會自動產生一個預設的建構函式,但只要我們定義了一個建構函式,系統就不會自動生成預設的建構函式。

7.無參的建構函式和全預設值得建構函式都認為是預設建構函式,並且預設的建構函式只能有一個

無參建構函式

class Date
{
public:
    //1.無參建構函式
    Date()
    {}
    //2.帶參建構函式
    Date(int year,int month,int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};

void TestDate()
{
    Date date1; //呼叫無參建構函式
    Date date2(2018,8,15); //呼叫帶參的建構函式
    Date date3(); //注意這裡沒有呼叫date3的建構函式定義出 date3
}

帶預設參的建構函式

class Date
{
public:
    //3.預設引數的建構函式
    Date(int year=2000,int month=1.int day=1)
    {
        _year = year;    
        _minth = month;
        _day = day;
    }
    //4.半預設引數的建構函式(不推薦)
    Date(int year,int month=1)
    {
        _year = year;
        _month = month;
        _day = 1;
    }
private:
    int _year;
    int _month;
    int _day;
};

void Test()
{
    Date date1; //呼叫預設建構函式
    Date date2(2015,1,2); //呼叫預設建構函式
}

解構函式

當一個物件的生命週期結束的時候,C++編譯系統會自動呼叫一個成員函式,這個成員函式即解構函式

特徵:

1.解構函式名是在類名前加上字元 ~;

2.無引數無返回值;

3.一個類有且只有一個解構函式(說明解構函式不能過載);(若未顯示定義,系統會自動生成預設的解構函式)

4.物件生命週期結束時,C++編譯系統自動呼叫解構函式;

class Date
{
public:
    //解構函式
    ~Date()
private:
    int _year;
    int _month;
    int _day;
};

解構函式內部不是刪除物件,而是做一些刪除物件前的清理工作

class MyVector
{
public:
    MyVector(int size)
    {
        _ptr = (int *)malloc(size*sizeof(int));
    }
    //這裡解構函式需要完成清(shi)理(fang)工(kong)作(jian).
    ~MyVector()
    {
        if(_ptr)
        {
            free(_ptr); //釋放堆上的空間
            _ptr = 0;   //將指標置空
        }
    }
private:
    int *_ptr;
};

類的拷貝建構函式

建立物件時使用同類物件來進行初始化,這時所使用的建構函式稱為拷貝建構函式

特徵:

1.拷貝建構函式是建構函式的一個過載

2.拷貝建構函式的引數只有一個且必須使用引用傳參,使用傳值會引發無窮遞迴

3.若未顯示定義,系統會預設生成預設的拷貝建構函式。預設的拷貝建構函式會按照成員的宣告順序依次拷貝類成員進行初始化

class Date
{
public:
    Date()
    {}
    //拷貝建構函式
    Date(const Date &d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
private:
    int _year;
    int _month;
    int _day;
};

void TestDate1()
{
    Date date1;
    //下面兩種用法都是呼叫拷貝建構函式,是等價的
    Date date2(date1);  //呼叫拷貝建構函式
    Date date3 = date1;  //呼叫拷貝建構函式
}

問題來了,為什麼拷貝建構函式的引數使用傳值會引發無窮遞迴?

問題是這樣,我們傳值進去的候,會先拷貝出一個臨時變數,來進行初始化,而這個過程就是拷貝建構函式的過程,所以會一直在呼叫拷貝建構函式,引發無窮遞迴

運算子過載

為了增強程式的可讀性,C++支援運算子過載

特徵:

1.operator+合成的運算子 構成函式名(operator<)

2.過載運算子以後,不能改變預算符的優先順序/結合性/運算元個數

class Date
{
public:
    //建構函式
    Date()
    {}
    //拷貝建構函式
    Date(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    // == 操作符的過載
    bool operator == (const Date& d)
    {
        return _year == d._year;
            && _month = d._month;
            && _day = d._day;
    }
private:
    int _year;
    int _month;
    int _day;
};

void Test()
{
    Date date1;
    Date date2 = date1;  //呼叫拷貝建構函式
    date2 == date1;      // 呼叫 == 運算子過載
}

這個地方我們要注意,在寫操作符過載函式的時候,引數中我們可以傳值,但為什麼要傳引用,因為傳值進去的話,在函式體內會先拷貝一份臨時變數,但是傳引用就不需要,省去了這個步驟,因為傳引用基本上就和傳地址是類似的,但是,我們得保證傳進去的形參不改變實參的具體內容,所以得加上 const 修飾

另外,我們看看編譯器對 == 操作符的過載處理過程:

我們在使用 ==操作符過載時,編譯器實際上幫我們加了一個引數,就是this指標,我們前面說過,this指標是隱式指標,我們看不見,但是我們要清楚,它一直指著物件。==操作符過載,我們需要兩個運算元,但是實際上我們只需要寫出一個,因為編譯器會幫我們把this指標加上。

另外,C++中有5個不能過載的操作符:

類的賦值操作符過載

1.賦值運算子的過載是對一個已存在的物件進行拷貝賦值

2.當程式沒有顯示地提供一個以本類或本類的引用為引數的賦值運算子過載函式時,編譯器會自動生成這樣一個賦值運算子過載函式

class Date
{
public:
    Date()
    {}
    //拷貝建構函式
    Date (const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    //賦值操作符過載
    Date& operator = (const Date& d)
    {
        if(this != &d)
        {
            this->_year = d._year;
            this->_month = d._month;
            this->_day = d._day;
        }
           return this;
    }
private:
    int _year;
    int _month;
    int _day;
};

void Test()
{
    Date date1;
    Date date2 = date1;  //呼叫拷貝建構函式
    Date date3;
    date3 = date1;  //呼叫賦值運算子過載
}

賦值操作符過載為什麼要返回一個引用呢?

為了進行連續賦值,即 x = y = z

1、賦值返回引用

x = y = z  先執行y = z,返回y的引用,執行x = y

2、賦值不返回引用

x = y = z  先執行y = z,返回用y初始化的臨時物件(注意臨時物件都是常物件),再執行x = y的臨時物件,返回用x初始化的臨時物件。

還有一點,if判斷不可少,這裡的判斷就是為了防止,將值賦給自己

類的const成員函式

const修飾普通變數

在C++中,const修飾的變數已經為一個常量,具有巨集的屬性,即在編譯的時候,編譯器會將const所修飾的常量進行替換

const修飾類成員

1.const修飾類成員變數時,該成員變數必須在建構函式的初始化列表中初始化、

2.const修飾類成員函式時,實際修飾該成員隱含的this指標,該成員函式不能對類的任何成員進行修改

void TestFunc()
{
    const int a = 10;
    int* pa = (int *)&a;

    *pa = 100;
    
    cout<<a<<endl;
    cout<<*pa<<endl;
}

在這段程式碼中,打印出a的值是 10 ,*pa 是 100

因為,a是一個常量10,在被const修飾的時候就具有了巨集的屬性,編譯的時候,我們就可以認為它是這樣:cout<<10<<endl;

下來我們看看編譯器對const修飾的成員函式的處理

有些時候,在const修飾的成員函式可能需要對類的某個成員變數進行修改,該成員變數只需被mutable關鍵字修飾即可

幾個問題:

const物件可以呼叫非const成員函式和const成員函式嗎?

非const物件可以呼叫非const成員函式和const成員函式嗎?

const成員函式內可以呼叫其他的const成員函式和非const成員函式嗎?

非const成員函式內可以呼叫其他的const成員函式和非const成員函式嗎?

我們只需要記住一點,const修飾的 我們可以理解為 小作用域 而非const修飾的 我們可以理解為大作用域

大作用域可以呼叫小作用域,而小作用域不能呼叫大作用域

類的取地址操作符過載 和 const修飾的取地址操作符過載

這兩個預設的成員函式一般不用重新定義,編譯器會預設生成

class Date
{
public:
    Date* operator &()
    {
        return this;
    }
    const Date* operator() const
    {
        return this;
    }
private:
    int _year;
    int _month;
    int _day;
};

只有在一種情況下,才需要你自己過載這兩個操作符,那就是你只想讓別人獲取到你指定的內容。

相關推薦

C++初識物件2

在之前的文章裡,我們出初步瞭解了,什麼是類,類如何定義,類的大小怎麼計算等 一個空類裡面什麼也沒有,但是它並非是什麼也沒有,只要是類,它就有6個預設的成員函式: 1.建構函式 2.解構函式 3.拷貝建構函式 4.賦值操作符過載 5.取地址操作符過載 6.co

C++】物件2

一、類的作用域 類定義了一個新的作用域,類的所有成員都在類的作用域中。在類體外定義成員,需要使用 :: 作用域解析符 指明成員屬於哪個類域。否則找不到。 class person { public: void PrintfPersonInfo(); private: char _name

Scala入門到精通——第六節物件

本節主要內容 1 類定義、建立物件 2 主構造器 3 輔助構造器 類定義、建立物件 //採用關鍵字class定義 class Person { //類成員必須初始化,否則會報錯 //這裡定義的是一個公有成員 var name:Strin

Scala入門到精通——第七節物件

本節主要內容 單例物件 伴生物件與伴生類 apply方法 應用程式物件 抽象類 單例物件 在某些應用場景下,我們可能不需要建立物件,而是想直接呼叫方法,但是Scala語言並不支援靜態成員,Scala通過單例物件來解決該問題。單例物件的建立方式如下:

C++學習筆記——物件

設計一個類就是設計一個新的型別,應該考慮: 此型別的“合法值”是什麼? 此型別應該有什麼樣的函式和操作符? 新型別的物件該如何被建立和銷燬? 如何進行物件的初始化和賦值? 物件作為函式的引數如何以值傳遞? 誰將使用此型別的物件成員? 類定義的語法形式: clas

初識C++: 物件1

類和物件的初步認識: 類是物件的抽象,物件是類的具體例項。因為類是抽象的,所以類不佔用記憶體,而物件是具體的,佔用儲存空間。 我們都知道,C語言時面向過程的語言,它關注的是過程中的資料與方法。 C++是面向物件的語言,它關注的是物件的屬性與功能 用一張圖來理解類和物件

《Java從入門到失業》第四章物件4.2String

4.2String類        這一節,我們學習第一個類:String類。String翻譯成漢語就是“字串”,是字元的序列。我們知道,在Java中,預設採用Unicode字符集,因此字串就是Unicode字元的序列。例如字

C++物件&&實現OFFSETOF巨集&&THIS指標

一.目錄   1.物件的相關知識   2.類的定義   3.類的例項化   4.類物件模型   5.模擬實現offsetof巨集   6.this指標 二.正文 1.物件的相關知識   C語言是面向過程的,關注的是過程,分析求解問題的步驟,通過函式呼叫逐步解決問題

C++物件

1.物件的相關知識   C語言是面向過程的,關注的是過程,分析求解問題的步驟,通過函式呼叫逐步解決問題。   C++是面向物件的,關注的是物件,將一件事拆分成不同的物件,靠物件之間的互動完成。   物件:任何一個物件都應該具有兩個要素,即屬性和行為,物件是由一組屬性和行為構成的。如現實生活中的手機就是一個物

C++物件&&實現OFFSETOF巨集&&THIS指標

一.目錄   1.物件的相關知識   2.類的定義   3.類的例項化   4.類物件模型   5.模擬實現offsetof巨集   6.this指標 二.正文 1.物件的相關知識   C語言是面向過程的,關注的是過程,分析求解問題的步驟,通過函式呼叫逐步解

C++ 物件

目錄 類 類的引入 類的定義 訪問限定符 封裝 類的作用域 類的例項化 引出 C語言是面向過程的,關注的是過程,分析出求解問題的步驟,通過函式呼叫逐步解決問題。C++是基於面向物件的,關注的是物件,將一件事情拆分成不同的物件,靠物

C#物件——部分類

        partial關鍵字允許把類、結構、方法或介面放在多個檔案中。一般情況下,某種型別的程式碼生成器生成了一個類的某部分,所以把類放在多個檔案中是有益的。假定要給類新增一些從工具中自動生成的內容。如果重新執行該工具,前面所做的修改就會丟失。partial關鍵字有

C++】物件4

一、類的六個預設成員函式 下面是一個Date類,但是它類中什麼成員也沒有,這就是空類。但是它雖然看起來什麼都沒有,實際上不是的,在我們什麼都不寫的情況下,它會自動生成六個預設的成員函式。如圖所示的建構函式、解構函式、拷貝建構函式、賦值過載函式、普通物件取地址函式、const物件取地址函式這六個函

C++】物件3—>> this指標

一、this指標的引入 我們在現實中,定義一個類都是根據一個需求來定義的。所以先來看一個常用的日期類 Date 。 class Date { public: void Print() { cout << _year << "-" << _month

Java 7物件域、引數、初始化

面向物件程式設計:每個物件包含對使用者公開的特定功能部分和隱藏的實現部分,在OOP中不必關心物件的具體實現,OOP更看重資料(結構),而不是具體演算法。 封裝(資料隱藏):將資料和行為組合在一個包裡,並對物件的使用者隱藏資料的實現方式,封裝的關鍵是絕不能讓其他類直接訪問例項

C++第八章 物件

【專案1 - 三角形類】下面設計一個三角形類,請給出各成員函式的定義 #include<iostream> #include<cmath> using namespace std; class Triangle {public: void Set

C++】物件

 一、this指標  關於this指標的一個精典回答:       當你進入一個房子後,       你可以看見桌子、椅子、地板等,       但是房子你是看不到全貌了。       對於一個類的例項來說,       你可以看到它的成員函式、成員變數,       但是例

《Java從入門到失業》第四章物件4.4方法引數及傳遞

4.4方法引數及傳遞        關於這個知識點,我想了很久該不該在這裡闡述。因為這個知識點稍微有點晦澀,並且就算不了解也不影響用Java編寫程式碼。不過筆者剛開始工作的時候,就是因為這塊內容沒有過多的關注,以至於相當於長一段時間對這塊內容都模糊不

《Java從入門到失業》第四章物件4.5

4.5包        前面我們已經聽過包(package)這個概念了,比如String類在java.lang包下,Arrays類在java.util包下。那麼為什麼要引入包的概念呢?我們思考一個問題:java類庫提供了上千個類,我們很難完全記住他們

《Java從入門到失業》第四章物件4.6路徑

4.6類路徑 4.6.1什麼是類路徑        前面我們討論過包,知道位元組碼檔案最終都會被放到和包名相匹配的樹狀結構子目錄中。例如上一節的例子:     其實類還有一種存放方式,就是可以歸檔到一個jar檔案中,jar檔案其實就是把位元