日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C++?primer類的基礎精講_C 語言

作者:撲街男孩 ? 更新時間: 2022-08-23 編程語言

定義抽象數據類型

初探this和

struct Sales_data
{
    string isbn(){return bookNo;}
    Sales_data & combine(const Sales_data&);
    double avg_price() const;
    string bookNo;
    unsigned units_sold=0;
    double revenue=0;
};
Sales_data total;

引入this

對于isbn成員函數的調用: total.isbn();

當我們調用成員函數時,實則上是在替某個對象調用它。在上面的調用中,當isbn返回bookNo時,實際上隱式地返回total.bookNo.

成員函數通過一個名為this的額外隱式參數來訪問調用它的那個對象。當我們調用一個成員函數時,用請求該函數的對象地址初始化this。例如,如果調用total.isbn(),編譯器負責把total的地址傳遞給isbn的隱式形參this,可以等價認為編譯器將該調用重寫成Sales_data::isbn(&total),調用Sales_data時的isbn成員時傳入了total的地址。

在成員函數內部,我們可以直接使用調用該函數的對象的成員,而無須通過成員訪問運算符來做到這一點,因為this所指的正是這個對象。任何對類成員的直接訪問都被看做this的隱式調用,例如,isbn在使用bookNo時,隱式地使用this指向的成員,就如同this->bookNo一樣。

構造函數

定義:類通過一個或幾個特殊的成員函數來控制其對象的初始化,這些函數叫做構造函數。

無論何時,只要類的對象被創建,就會執行構造函數。

構造函數的名字和類名一樣,構造函數沒有返回類型,一個類可以擁有多個構造函數,但每個構造函數之間必須在參數數量或參數類型上存在不同。且構造函數不能被聲明成const。

當一個類沒有定義任何構造函數時,編譯器會給類自動添加一個默認構造函數,該構造函數無須任何實參對對象進行初始化。

對前面的Sales_data類進行編寫構造函數

struct Sales_data
{
    Sales_data()=default;
    Sales_data(const string &s):bookNo(s)()
    Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(istream &)
    string isbn() const{return bookNo;}
    Sales_data &combine(const Sales_data&);
    double avg_price() const;
    string bookNo;
    unsigned units_sold=0;
    double revenue=0.0;
}

(1)=default的含義

如果需要默認構造函數起作用,那么可以在參數列表后面寫上=default來要求編譯器生成默認構造函數。

(2)構造函數初始值列表

Sales_data(const string &s):bookNo(s)()
Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}

上面出現了兩個新的構造函數的寫法,該部分稱為構造函數初始值列表。

負責為新創建的對象的一個或幾個數據成員賦初值。構造函數初始值是成員名字的一個列表,每個名字后面緊跟括號括起來的成員初始值。

當某個數據成員被構造函數初始值列表忽略時,他將以合成默認構造函數相同的方式隱式初始化。所以,第一個構造函數等價于:

Sales_data(const string &s):bookNo(s),units_sold(0),revenue(0)();

訪問控制和封裝

訪問控制符public和private

定義在public說明符之后的成員在整個程序可被訪問。

定義在private說明符之后的成員可以被類的成員函數訪問,但是不能被使用該類的代碼訪問。

Sales_data類的新形式

class Sales_data
{
public:
    Sales_data()=default;
    Sales_data(const string &s):bookNo(s)()
    Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(istream &)
    string isbn() const{return bookNo;}
    Sales_data &combine(const Sales_data&);
private:
    double avg_price() const;
    string bookNo;
    unsigned units_sold=0;
    double revenue=0.0;
}

友元

類允許其他類或者函數訪問他的非公有成員,方法是令其他類或者函數成為他的友元。如果一個類想把一個函數作為他的友元,只需要增加一個friend關鍵字開始的函數聲明語句即可

class Sales_data
{
    friend Sales_data add(const Sales_data &,const Sales_data&);
    friend istream &read(istream&,Sales_data&);
    friend ostream &print(ostream&,const Sales_data&)
public:
    Sales_data()=default;
    Sales_data(const string &s):bookNo(s)()
    Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(istream &)
    string isbn() const{return bookNo;}
    Sales_data &combine(const Sales_data&);
private:
    double avg_price() const;
    string bookNo;
    unsigned units_sold=0;
    double revenue=0.0;
}

友元的聲明只能出現在類定義的內部。友元不是類的成員,也不受他所在區域訪問控制級別的約束。

類的其他特性

可變數據成員

在一個const成員函數中,若希望修改類的某個數據成員,可以通過在變量的聲明中加入mutable關鍵字實現

class screen{
public:
        void some_menmber() const;
private:
        mutable size_t access_ctr
};
void screen::some_member() const
{
    ++access_ctr
}

返回*this的成員函數

inline Screen &Screen::set(char c)
{
    contents[cursor]=c;
    return *this;
}
inline Screen &Screen::set(pos r,pos col,char ch)
{
    contents[r*width+col]=ch;
    return *this;
}
inline Screen &Screen::move(pos r,pos c)
{
    pos row=r*width;
    cursor=row+c;
    return *this;
}

move和set一樣,返回的值是對對象的引用。

myScreen.move(4,0).set('#');

等同于

myScreen.move(4.0);

myScreen.set('#');

假如我們定義的返回類型不是引用,則move的返回值將是*this的副本,因此調用set只能改變臨時副本,不能改變myScreen的值

友元類

例如,window_mgr類的某些成員需要訪問screen類的內部數據,例如window_mgr的clear函數將一個指定的screen類的內容設置為空白。

class Screen{
//window_mgr的成員可以訪問Screen類的私有部分
friend class Window_mgr
//Screen類剩余部分
}
class Window_mgr{
public:
    using ScreenIndex=std::vector<Screen>::size_type
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{Screen(24,80,' ')}
};
void Window_mgr::clear(ScreenIndex)
{
    Screen &s=screen[i];
    s.contens=string(s.height*swidth,' ')
}

構造函數再探

構造函數初始值列表

(1)構造函數的初始值有時必不可少

如果成員是const或者是引用的話,必須將其初始化

class ConstRef
{
public:
    ConstRef(int ii);
private:
    int i;
    const int ci;
    int &ri;
};

成員ci和ri必須被初始化,如果沒有為他們提供構造函數初始值的話將引發錯誤。正確形式應該是: ConstRef::ConstRef(int ii):i(ii),ci(ii),ri(i){};

如果成員是const,引用,或者屬于某種未提供默認構造函數的類類型,我們必須通過構造函數初始值列表為這些成員提供初值。

(2)成員初始化順序

成員初始化的順序與他們在類定義中的出現順序一致。一般來說初始化的順序沒什么特別要求,不過如果一個成員是用另一個成員來初始化的,那么著兩個成員的初始化順序就關鍵了。

例如

class X
{
    int i;
    int j;
public:
    X(int val):j(val),i(j){}
};

而編譯器實際上是先初始化i,在初始化j,而初始化i的時候發現j沒有值,所以上述構造函數會發生錯誤。

默認構造函數的作用

當對象被默認初始化或值初始化時自動執行默認構造函數。

默認初始化在以下情況下發生:

(1)當我們在塊作用域內不適用任何初始值定義一個非靜態變量或數組時。

(2)當一個類本身含有類類型的成員且使用合成的默認構造函數時

(3)當類類型的成員沒有在構造函數初始值列表中顯示地初始化時

值初始化在以下情況發生:

(1)在數組初始化的過程中如果我們提供的初始值數量少于數組的大小時

(2)當我們不適用初始值定義一個局部靜態變量

使用默認構造函數

下面的obj的聲明可以正常通過

Sales_data obj();//正確,定義了一個函數而非對象
if(obj.isbn()==primer_5th_ed.isbn())//錯誤:obj是一個函數

但當我們試圖使用obj時,編譯器將報錯,提示我們不能使對函數使用成員訪問運算符。因為obj的實際含義是一個不接受任何參數的函數并且其返回值是Sales_data類型的對象。

如果想定義一個使用默認構造函數進行初始化的對象,正確方法是去掉對象名之后的空括號對。

Sales_data obj;

聚合類

滿足一下條件的類是聚合類:

(1)所有成員都是public的

(2)沒有定義任何構造函數

(3)沒有類內初始值

(4)沒有基類,也沒有虛函數

例:

struct Data
{
    int val;
    string s;
};

類的靜態成員

class Account
{
public:
    void calculate(){amount+=amount*interestRate;}
    static double rate(){return interestRate;}
    static void rate(double);
private:
    string owner;
    double amount;
    static double interestRate;
    static double initRate();
}

類的靜態成員存在于任何對象之外,對象中不包含任何與靜態數據成員有關的數據,因此,每個Account對象將包含兩個數據成員:owner和amout。只存在一個interestRate對象而且他被所有account對象共享。

靜態成員函數也不與任何對象綁定在一個,他們不包含this指針。作為結果,靜態成員函數不能聲明成const的,而且我們也不能在static函數體內使用this指針。

原文鏈接:https://blog.csdn.net/weixin_42375706/article/details/121008910

欄目分類
最近更新