第7章:類(lèi)

  • #1.定義抽象數(shù)據(jù)類(lèi)型
    • 1.1 設(shè)計(jì)Sales_data類(lèi)
    • 1.2 定義改進(jìn)的Sales_data類(lèi)
    • 1.3 定義類(lèi)相關(guān)的成員函數(shù)
    • 1.4 構(gòu)造函數(shù)
    • 1.5 拷貝、賦值和析構(gòu)
  • #2.訪問(wèn)控制和封裝
    • 2.1 友元
  • #3.類(lèi)的其他特性
    • 3.1 類(lèi)成員再探
    • 3.2 返回*this的成員函數(shù)
    • 3.3 類(lèi)類(lèi)型
    • 3.4 友元再探
  • #4.類(lèi)的作用域
    • 4.1 名字查找和類(lèi)的作用域
  • #5.構(gòu)造函數(shù)再探
    • 5.1 構(gòu)造函數(shù)初始值列表
    • 5.2 委托構(gòu)造函數(shù)
    • 5.3 默認(rèn)構(gòu)造函數(shù)的作用
    • 5.4 隱式的類(lèi)類(lèi)型轉(zhuǎn)換
    • 5.5 聚合類(lèi)
    • 5.6 字面值常量類(lèi)
  • #6.類(lèi)的靜態(tài)成員

類(lèi)的基本思想是數(shù)據(jù)抽象封裝,數(shù)據(jù)抽象是一種依賴(lài)于接口實(shí)現(xiàn)分離的編程技術(shù)蒲犬。

封裝實(shí)現(xiàn)了類(lèi)的接口和實(shí)現(xiàn)分離莺琳。封裝后的類(lèi)隱藏了實(shí)現(xiàn)細(xì)節(jié)囚似,類(lèi)的用戶(hù)只能使用接口而無(wú)法訪問(wèn)實(shí)現(xiàn)部分倒堕。

類(lèi)要想實(shí)現(xiàn)數(shù)據(jù)抽象和封裝,需要首先定義一個(gè)抽象數(shù)據(jù)類(lèi)型钧舌。

#1. 定義抽象數(shù)據(jù)類(lèi)型

要想定義抽象數(shù)據(jù)類(lèi)型,我們需要定義一些操作以供類(lèi)的用戶(hù)使用涎跨。一旦類(lèi)定義了自己的操作洼冻,我們就可以封裝它的數(shù)據(jù)成員。

1.1 設(shè)計(jì)Sales_data類(lèi)

struct Sales_data {
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

1.2 定義改進(jìn)的Sales_data類(lèi)

定義和聲明成員函數(shù)的方式和普通函數(shù)類(lèi)似差不多隅很。成員函數(shù)的聲明必須在類(lèi)的內(nèi)部撞牢,它的定義則既可以在類(lèi)的內(nèi)部也可以在類(lèi)的外部。作為接口組成部分的非成員函數(shù)外构,它們的定義和聲明都在類(lèi)的外部普泡。

struct Sales_data {
    //新成員:關(guān)于Sales_data對(duì)象的操作
    std::string isbn() const { return bookNo; }
    Sales_data &combine(const Sales_data &);
    double avg_price() const;
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
//Sales_data的非成員接口函數(shù)
Sales_data add(const Sales_data &,const Sales_data &);
std::ostream &print(std::ostream &,const Sales_data &);
std::istream &read(std::istream &,Sales_data &);

==定義在類(lèi)內(nèi)部的函數(shù)都是隱式的inline函數(shù)。==

定義成員函數(shù)

所有的成員都必須在類(lèi)的內(nèi)部聲明审编,但是成員函數(shù)體可以定義在類(lèi)內(nèi)也可以定義在類(lèi)外撼班。以Sales_data的成員函數(shù)isbn為例來(lái)引出this指針:

std::string isbn() const {
    return bookNo;
}

isbn函數(shù)返回Sales_data的數(shù)據(jù)成員bookBo。

引入this

成員函數(shù)通過(guò)一個(gè)名為this的額外的隱式參數(shù)來(lái)訪問(wèn)調(diào)用它的那個(gè)對(duì)象垒酬。當(dāng)我們調(diào)用一個(gè)成員函數(shù)時(shí)砰嘁,用請(qǐng)求該函數(shù)的對(duì)象地址初始化this件炉。例如,如果調(diào)用:

total.isbn();

則編譯器負(fù)責(zé)把total的地址傳遞給isbn的隱式形參this矮湘≌迕幔可以等價(jià)地認(rèn)為編譯器將該調(diào)用重寫(xiě)成了如下形式:

Sales_data::isbn(&total);

其中,調(diào)用Sales_data的isbn成員時(shí)傳入了total地址缅阳。在成員函數(shù)內(nèi)部磕蛇,我們可以直接使用調(diào)用該函數(shù)的對(duì)象的成員,而不須通過(guò)成員訪問(wèn)運(yùn)算符來(lái)做到這一點(diǎn)十办,因?yàn)閠his所指向的正是這個(gè)對(duì)象秀撇。任何對(duì)類(lèi)的成員的直接訪問(wèn)都被看作this的隱式引用。對(duì)于我們來(lái)說(shuō)向族,this形參是隱式定義的呵燕。我們可以把isbn定義成如下形式:

std::string isbn() const {
    return this->bookNo;
}

因?yàn)閠his的目的總是指向這個(gè)對(duì)象,所以this是一個(gè)常量指針件相。

引入const成員函數(shù)

isbn函數(shù)的另一個(gè)關(guān)鍵之處是緊隨參數(shù)列表之后的const關(guān)鍵字再扭,這里,const關(guān)鍵字的作用是修改隱式this指針的類(lèi)型夜矗。默認(rèn)情況下泛范,this的類(lèi)型是指向類(lèi)類(lèi)型非常量版本的常量指針。C++語(yǔ)言允許我們把const關(guān)鍵字放在成員函數(shù)的參數(shù)列表之后侯养,此時(shí)敦跌,緊跟在參數(shù)列表后面的const表示this是一個(gè)指向常量的指針。像這樣使用const的成員函數(shù)被稱(chēng)作常量成員函數(shù)逛揩。

//偽代碼柠傍。說(shuō)明隱式的this指針如何使用的
//下面代碼是非法的,因?yàn)槲覀儾荒茱@式的定義自己的this指針
//謹(jǐn)記此處的this是一個(gè)指向常量的指針辩稽,因?yàn)閕sbn是一個(gè)常量成員
std::string Sales_data::isbn(const Sales_data *const this) const { 
    return this->bookNo; 
} 

==常量對(duì)象惧笛,以及常量對(duì)象的引用或指針都只能調(diào)用常量成員函數(shù)。==

類(lèi)作用域和成員函數(shù)

編譯器分兩步處理類(lèi):首先編譯成員的聲明逞泄,然后再是函數(shù)體患整。因此,成員函數(shù)體可以隨意使用類(lèi)的其他成員而無(wú)須在意這些成員的出現(xiàn)的次序喷众。

在類(lèi)的外部定義成員函數(shù)

當(dāng)我們?cè)陬?lèi)的外部定義成員函數(shù)時(shí)各谚,成員函數(shù)的定義必須與它的聲明匹配,同時(shí)到千,類(lèi)外部定義的成員的名字必須包含它所屬的類(lèi)名:

double Sales_data::avg_price() const {
    if (units_sold) {
        return revenue / units_sold;
    }else {
        return 0;
    }
}
定義一個(gè)返回this對(duì)象的函數(shù)
Sales_data &Sales_data::combine(const Sales_data &rhs) {
    units_sold += rhs.units_sold; //把rhs的成員加到this對(duì)象的成員上
    revenue += rhs.revenue;
    return *this; //返回調(diào)用該函數(shù)的對(duì)象
}

1.3 定義類(lèi)相關(guān)的非成員函數(shù)

我們定義非成員函數(shù)的方式和其他函數(shù)一樣昌渤,通常把函數(shù)的聲明和定義分離開(kāi)來(lái)。如有函數(shù)在概念是屬于類(lèi)但是不定義在類(lèi)中憔四,則它一般應(yīng)與類(lèi)聲明在同一個(gè)頭文件內(nèi)膀息。

定義read和print函數(shù)
std::istream &read(std::istream &is, Sales_data &item) {
    double price = 0.0;
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}
std::ostream &print(std::ostream &os, const Sales_data &item) {
    os << item.isbn() << " " << item.units_sold 
        << " " << item.revenue << " " << item.avg_price();
    return os;
}
定義add函數(shù)
Sales_data add(const Sales_data &lhs, const Sales_data &rhs) {
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;
}

1.4 構(gòu)造函數(shù)

每個(gè)類(lèi)都分別定義了它的對(duì)象被初始化的方式般眉,類(lèi)通過(guò)一個(gè)或幾個(gè)特殊的成員函數(shù)來(lái)控制其對(duì)象的初始化過(guò)程,這些函數(shù)叫做構(gòu)造函數(shù)潜支。構(gòu)造函數(shù)的任務(wù)是初始化類(lèi)對(duì)象的數(shù)據(jù)成員甸赃,無(wú)論何時(shí)只要類(lèi)的對(duì)象被創(chuàng)建,就會(huì)執(zhí)行構(gòu)造函數(shù)冗酿。

構(gòu)造函數(shù)的名字與類(lèi)名相同埠对。和其他函數(shù)不一樣的是,構(gòu)造函數(shù)沒(méi)有返回類(lèi)型裁替;構(gòu)造函數(shù)不能被聲明成const鸠窗。

合成的默認(rèn)構(gòu)造函數(shù)

類(lèi)通過(guò)一個(gè)一個(gè)特殊的構(gòu)造函數(shù)來(lái)控制默認(rèn)初始化過(guò)程,這個(gè)函數(shù)叫做默認(rèn)構(gòu)造函數(shù)胯究。默認(rèn)構(gòu)造函數(shù)無(wú)須任何實(shí)參。編譯器創(chuàng)建的構(gòu)造函數(shù)又被稱(chēng)為合成的默認(rèn)構(gòu)造函數(shù)躁绸。

==只有當(dāng)類(lèi)沒(méi)有聲明任何構(gòu)造函數(shù)時(shí)裕循,編譯器才會(huì)自動(dòng)生成默認(rèn)構(gòu)造函數(shù)。==

某些類(lèi)不能依賴(lài)于合成的默認(rèn)構(gòu)造函數(shù)

對(duì)于一個(gè)普通的類(lèi)來(lái)說(shuō)净刮,必須定義它自己的默認(rèn)構(gòu)造函數(shù)剥哑,原因有三:

  1. 只有當(dāng)類(lèi)沒(méi)有聲明任何構(gòu)造函數(shù)時(shí),編譯器才會(huì)自動(dòng)地生成默認(rèn)構(gòu)造函數(shù)淹父。一旦在類(lèi)中定義了其他構(gòu)造函數(shù)株婴,除非我們?cè)俣x默認(rèn)構(gòu)造函數(shù),否則類(lèi)沒(méi)有默認(rèn)構(gòu)造暑认。
  2. 對(duì)于某些類(lèi)而言困介,合成的默認(rèn)構(gòu)造可能執(zhí)行錯(cuò)誤的操作。如果類(lèi)中的內(nèi)置類(lèi)型或復(fù)合類(lèi)型的對(duì)象被默認(rèn)初始化蘸际,它們的值將是未定義的座哩。
  3. 編譯器有時(shí)候不能為類(lèi)合成默認(rèn)的構(gòu)造函數(shù)。例如:類(lèi)中包含了一個(gè)其他類(lèi)型的成員粮彤,而這個(gè)成員沒(méi)有默認(rèn)構(gòu)造函數(shù)根穷。
定義Sales_data的構(gòu)造函數(shù)
struct Sales_data {
    //C++11新標(biāo)準(zhǔn)中,如果我們需要默認(rèn)的行為导坟,可以在參數(shù)列表后寫(xiě)上=default來(lái)要求編譯器生成默認(rèn)構(gòu)造
    Sales_data() = default;
    Sales_data(const std::string &s) :bookNo(s) {}
    Sales_data(const std::string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(p*n) {}
    Sales_data(std::iostream &);
    std::string isbn() const {
        return bookNo;
    }
    Sales_data &combine(const Sales_data &);
    double avg_price() const;
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
=default的含義

在C++11新標(biāo)準(zhǔn)中屿良,如果我們需要默認(rèn)的行為,可以通過(guò)在參數(shù)列表后寫(xiě)上=default來(lái)要求編譯器生成構(gòu)造函數(shù)惫周。

構(gòu)造函數(shù)初始值列表
Sales_data(const std::string &s) :bookNo(s) {}
Sales_data(const std::string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(p*n) {}

這兩個(gè)定義中出現(xiàn)了新的部分尘惧,即冒號(hào)以及冒號(hào)和花括號(hào)之間的代碼,其中花括號(hào)定義了函數(shù)體闯两。我們把新出現(xiàn)的部分稱(chēng)為構(gòu)造函數(shù)初始值列表褥伴。它負(fù)責(zé)為新創(chuàng)建的對(duì)象的一個(gè)或幾個(gè)數(shù)據(jù)成員賦初值谅将。構(gòu)造函數(shù)初始值是成員名字的一個(gè)列表,每個(gè)名字后面緊跟括號(hào)括起來(lái)的(或者在花括號(hào)內(nèi)的)成員初始值重慢。不同成員初始化通過(guò)逗號(hào)分隔開(kāi)來(lái)饥臂。

在類(lèi)的外部定義構(gòu)造函數(shù)

與其他幾個(gè)構(gòu)造函數(shù)不同,以istream為參數(shù)的構(gòu)造函數(shù)需要執(zhí)行一些實(shí)際操作似踱。在它的函數(shù)體內(nèi)隅熙,調(diào)用了read函數(shù)以給數(shù)據(jù)成員賦以初值:

Sales_data::Sales_data(std::iostream &is) {
    read(is, *this);
}

當(dāng)我們?cè)陬?lèi)的外部定義構(gòu)造函數(shù)時(shí),必須指明該構(gòu)造函數(shù)是哪個(gè)類(lèi)的成員核芽。因此囚戚,需要指定具體作用域。

1.5 拷貝轧简、賦值和析構(gòu)

除了定義類(lèi)的對(duì)象如何初始化之外驰坊,類(lèi)還需要控制拷貝、賦值和銷(xiāo)毀對(duì)象時(shí)發(fā)生的行為哮独。

某些類(lèi)不能依賴(lài)于合成的版本

盡管編譯器能替我們合成拷貝拳芙、賦值和銷(xiāo)毀的操作,但是必須清楚的一點(diǎn)是皮璧,對(duì)于某些類(lèi)來(lái)說(shuō)合成的版本無(wú)法正常工作舟扎。


#2. 訪問(wèn)控制與封裝

c++語(yǔ)言中,使用訪問(wèn)說(shuō)明符加強(qiáng)類(lèi)的封裝性:

  • 定義在public說(shuō)明符之后的成員在整個(gè)程序內(nèi)可被訪問(wèn)悴务,public成員定義類(lèi)的接口睹限。
  • 定義在private說(shuō)明符之后的成員可以被類(lèi)的成員函數(shù)訪問(wèn),但是不能被使用該類(lèi)的代碼訪問(wèn)讯檐,private部分封裝了類(lèi)的實(shí)現(xiàn)細(xì)節(jié)羡疗。
使用class或struct關(guān)鍵字

使用class和struct定義類(lèi)唯一的區(qū)別就是默認(rèn)的訪問(wèn)權(quán)限。使用struct關(guān)鍵字别洪,則定義在第一個(gè)訪問(wèn)說(shuō)明符之前的成員是public顺囊;如果使用class關(guān)鍵字,則成員是private的蕉拢。

==使用class和struct定義類(lèi)唯一的區(qū)別就是默認(rèn)的訪問(wèn)權(quán)限特碳。==

2.1 友元

類(lèi)可以允許其他類(lèi)或者函數(shù)訪問(wèn)它的非公有成員,方法是令其他類(lèi)或者函數(shù)成為它的友元晕换。如果類(lèi)想把一個(gè)函數(shù)作為它的友元午乓,只需要增加一條以friend關(guān)鍵字開(kāi)始的函數(shù)聲明即可:

class Sales_data {
//為Sales_data的非成員函數(shù)所做的友元聲明
friend Sales_data add(const Sales_data &, const Sales_data &);
friend std::ostream &print(std::ostream &, const Sales_data &);
friend std::istream &read(std::istream &, Sales_data &);
public:
    Sales_data() = default;
    Sales_data(const std::string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(n*p) {}
    Sales_data(const std::string &s) :bookNo(s) {}
    Sales_data(std::iostream &);
    std::string isbn() const {
        return bookNo;
    }
    Sales_data &combine(const Sales_data &);
private:
    double avg_price() const {
        return units_sold ? revenue / units_sold : 0;
    }
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
//Sales_data接口的非成員接口的聲明
Sales_data add(const Sales_data &, const Sales_data &);
std::ostream &print(std::ostream &, const Sales_data &);
std::istream &read(std::istream &, Sales_data &); 

==一般來(lái)說(shuō),最好在類(lèi)定義開(kāi)始或結(jié)束前的位置集中聲明友元闸准。==

友元的聲明

友元的聲明僅僅指定了訪問(wèn)的權(quán)限益愈,而非一個(gè)通常意義上的函數(shù)聲明。如果我們希望用戶(hù)能夠調(diào)用某個(gè)友元函數(shù),那么我們必須在友元聲明之外再專(zhuān)門(mén)對(duì)函數(shù)進(jìn)行一次聲明蒸其。


#3. 類(lèi)的其他特性

3.1 類(lèi)成員再探

定義一個(gè)類(lèi)型成員
class Screen {
public:
    typedef std::string::size_type pos; //pos為類(lèi)型成員
private:
    pos cursor = 0;
    pos height = 0, width = 0;
    std::string contents;
};

用來(lái)定義類(lèi)型的成員必須先定義后使用敏释。

Screen類(lèi)的成員函數(shù)
class Screen {
 public:
    typedef std::string::size_type pos;
    Screen() = default;
    Screen(pos ht,pos wd,char c):height(ht),width(wd),
    contents(ht * wd,c){}
    char get() const { //讀取光標(biāo)處的字符
        return contents[cursor]; //隱式內(nèi)聯(lián)
    }
    inline char get(pos ht,pos wd) const; //顯示內(nèi)聯(lián)
    Screen &move(pos r,pos c);

private:
    pos cursor = 0;
    pos height = 0,width = 0;
    std::string contents;
};
令成員作為內(nèi)聯(lián)函數(shù)

在類(lèi)中,常有一些規(guī)模較小的函數(shù)適合于被聲明成內(nèi)聯(lián)函數(shù)摸袁。定義在類(lèi)內(nèi)部的函數(shù)是自動(dòng)inline的钥顽。我們也可以在類(lèi)的外部用inline關(guān)鍵字修飾函數(shù)的定義:

inline Screen &Screen::move(pos r,pos c) {
    pos row = r * width; //計(jì)算行的位置
    cursor = row + c; //在行內(nèi)將光標(biāo)移動(dòng)到指定的列
    return *this; //以左值的形式返回對(duì)象
}

char Screen::get(pos r,pos c) const { //在類(lèi)的內(nèi)不聲明成inline
    pos row = r * width; //計(jì)算行的位置
    return contents[row + c]; //返回給定列的字符
}
重載成員函數(shù)

和非成員函數(shù)一樣,成員函數(shù)也可以被重載靠汁。只要函數(shù)之間在參數(shù)的數(shù)量和\或類(lèi)型上有區(qū)別就行蜂大。

可變數(shù)據(jù)成員

有時(shí)會(huì)發(fā)生這樣一種情況,我們希望修改類(lèi)的某個(gè)數(shù)據(jù)成員蝶怔,即使是在一個(gè)const成員函數(shù)內(nèi)奶浦。可以通過(guò)在變量的聲明中加入mutable關(guān)鍵字做到這一點(diǎn)踢星。

一個(gè)可變數(shù)據(jù)成員永遠(yuǎn)不會(huì)是const澳叉,即使它是const對(duì)象的成員。因此沐悦,一個(gè)const成員可以改變一個(gè)可變成員的值耳高。

class Screen {
public:
    void some_member() const;
private:
    mutable size_t access_ptr; //即使在一個(gè)const對(duì)象內(nèi)也能被修改
};
void Screen::some_member() const {
    ++access_ptr;
}

3.2 返回*this的成員函數(shù)

class Screen {
public:
    typedef std::string::size_type pos;
    Screen &set(char);
    Screen &set(pos,pos,char);
private:
    pos cursor;
    pos height = 0, width = 0;
    std::string contents;
};
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;
}

和move操作一樣,我們的set成員的返回值是調(diào)用set的對(duì)象的引用所踊。返回引用的函數(shù)是左值的,意味著這些函數(shù)返回的是對(duì)象本身而非對(duì)象的副本概荷。

從const成員函數(shù)返回*this

==一個(gè)const成員函數(shù)如果以引用的形式返回*this秕岛,那么它的返回類(lèi)型將是常量引用。==

基于const的重載

通過(guò)區(qū)分成員函數(shù)是否是const的误证,我們可以對(duì)其進(jìn)行重載继薛。

class Screen {
public:
    //根據(jù)對(duì)象是否是const重載了display函數(shù)
    Screen &display(std::ostream &os){
        do_display(os);
        return *this;
    }
    const Screen &display(std::ostream &os) {
        do_display(os);
        return *this;
    }

private:
    //該函數(shù)負(fù)責(zé)顯示Screen的內(nèi)容
    void do_display(std::ostream &os) const {
        os << contents;
    }
};

3.3 類(lèi)類(lèi)型

每個(gè)類(lèi)定義了唯一的類(lèi)型。對(duì)于兩個(gè)類(lèi)來(lái)說(shuō)愈捅,即使它們的成員完全一樣遏考,這兩個(gè)類(lèi)也是兩個(gè)不同的類(lèi)型。例如:

struct First {
    int memi;
    int getMem();
};
struct Second {
    int memi;
    int getMem();
};
First obj1;
Second obj2 = obj1; //錯(cuò)誤:obj1和obj2的類(lèi)型不同

==即使兩個(gè)類(lèi)的成員列表完全一致蓝谨,它們也是不同的類(lèi)型灌具。對(duì)于一個(gè)類(lèi)來(lái)說(shuō),它的成員和其他任何類(lèi)的成員都不是一回事兒譬巫。==

類(lèi)的聲明

就像可以把函數(shù)的聲明和定義分離開(kāi)來(lái)一樣咖楣,我們也能僅僅聲明類(lèi)而暫時(shí)不定義它:

class Screen; //Screen類(lèi)的聲明

這種聲明有時(shí)被稱(chēng)為前向聲明,對(duì)于類(lèi)型Screen來(lái)說(shuō)芦昔,在它聲明之后定義之前是一個(gè)不完全類(lèi)型诱贿。

3.4 友元再探

類(lèi)還可以把其他類(lèi)定義成友元,也可以把其他類(lèi)的成員函數(shù)定義成友元。此外珠十,友元函數(shù)能定義在類(lèi)的內(nèi)部料扰,這樣的函數(shù)是隱式內(nèi)聯(lián)的。

類(lèi)之間的友元關(guān)系
class Screen {
    //Window_mgr的成員可以訪問(wèn)Screen類(lèi)的私有部分
    friend class Window_mgr;
};

如果一個(gè)類(lèi)指定了友元類(lèi)焙蹭,則友元類(lèi)的成員函數(shù)可以訪問(wèn)此類(lèi)包括非公有成員在內(nèi)的所有成員晒杈。
每個(gè)類(lèi)負(fù)責(zé)控制自己的友元類(lèi)和友元函數(shù)。

令成員函數(shù)作為友元

除了令整個(gè)類(lèi)作為友元之外壳嚎,還可以只為類(lèi)的某個(gè)成員函數(shù)提供訪問(wèn)權(quán)限桐智。

class Screen {
    //Window_mgr::clear必須在Screen類(lèi)之前被聲明
    friend void Window_mgr::clear(ScreenIndex);  
};
函數(shù)重載和友元

盡管重載函數(shù)的名字相同,但它們?nèi)匀皇遣煌暮瘮?shù)烟馅。因此说庭,如果一個(gè)類(lèi)想把一組重載函數(shù)聲明成它的友元,它需要對(duì)這組函數(shù)中的每一個(gè)分別聲明:

//重載的storeOn函數(shù)
extern std::ostream &storeOn(std::ostream &,Screen &);
extern BitMap &storeOn(BitMap &,Screen &);

class Screen {
    //storeOn的ostream版本能訪問(wèn)Screen對(duì)象的私有部分
    friend std::ostream &storeOn(std::ostream &,Screen &);
    //...
};
友元聲明和作用域

類(lèi)和非成員函數(shù)的聲明不是必須在它們的友元之前聲明郑趁。友元聲明的作用是影響訪問(wèn)權(quán)限刊驴,它本身并非普通意義的聲明。

struct X {
    friend void f(); /*友元函數(shù)可以定義在函數(shù)的內(nèi)部*/
    X() { f(); }; //錯(cuò)誤:f還沒(méi)有被聲明
    void g();
    void h();
};
void X::g() { return f(); } //錯(cuò)誤:f還沒(méi)有被聲明
void f(); //聲明那個(gè)定義在X中的函數(shù)
void X::h() { return f();} //正確:現(xiàn)在f的聲明在作用域中了

#4. 類(lèi)的作用域

每個(gè)類(lèi)都會(huì)定義它自己的作用域寡润。在類(lèi)的作用域之外捆憎,普通的數(shù)據(jù)和函數(shù)成員只能由對(duì)象、引用或者指針使用成員訪問(wèn)運(yùn)算符來(lái)訪問(wèn)梭纹。對(duì)于類(lèi)類(lèi)型成員則使用作用域運(yùn)算符訪問(wèn)躲惰。

4.1 名字查找與類(lèi)的作用域

在目前為止,我們編寫(xiě)的程序中变抽,名字查找的過(guò)程比較直截了當(dāng):

  • 首先础拨,在名字所在的塊中尋找其聲明的語(yǔ)句,只考慮在名字的使用之前出現(xiàn)的聲明绍载。
  • 如果沒(méi)找到诡宗,繼續(xù)查找外層作用域。
  • 如果最終沒(méi)有找到匹配的聲明击儡,則程序報(bào)錯(cuò)塔沃。
    對(duì)于定義在類(lèi)外部的成員函數(shù)來(lái)說(shuō),解析其中名字的方式和上述查找規(guī)則有所區(qū)別阳谍。類(lèi)的定義分兩步處理:
  • 首先蛀柴,編譯成員的聲明。
  • 直到類(lèi)全部可見(jiàn)后才編譯函數(shù)體矫夯。

==編譯器處理完類(lèi)中的全部聲明后才會(huì)處理成員函數(shù)的定義名扛。==

用于類(lèi)成員聲明的名字查找

這種兩階段的處理方式只使用與成員函數(shù)中使用的名字。如果某個(gè)成員的聲明使用了類(lèi)中尚未出現(xiàn)的名字茧痒,則編譯器將在定義該類(lèi)的作用域中繼續(xù)查找肮韧。例如:

typedef double Money;
string bal;
class Account {
public:
    Money balance() {
        return bal;
    }
private:
    Money bal;
    //...
};

當(dāng)編譯器看到balance函數(shù)的聲明語(yǔ)句時(shí),它將在Account類(lèi)的范圍內(nèi)尋找對(duì)Money的聲明。如果沒(méi)找到弄企,編譯器會(huì)接著到Account的外層作用域中查找超燃。

類(lèi)型名要特殊處理

一般來(lái)說(shuō),內(nèi)層作用域可以重新定義外層作用域中的名字拘领,即使該名字已經(jīng)在內(nèi)層作用域中使用過(guò)意乓。然而在類(lèi)中,如果成員使用了外層作用域中的某個(gè)名字约素,而該名字代表一種類(lèi)型届良,則類(lèi)不能在之后重新定義該名字:

typedef double Money;
class Account {
public:
    Money balance() { //使用外層作用域的Money
        return bal;
    }
private:
    typedef double Money; //錯(cuò)誤:不能重新定義Money
    Money bal;
};

#5. 構(gòu)造函數(shù)再探

5.1 構(gòu)造函數(shù)初始值列表

如果沒(méi)有在構(gòu)造函數(shù)的初始值列表中顯式地初始化成員,則該成員將在函數(shù)體之前執(zhí)行默認(rèn)初始化圣猎。例如:

//Sales_data構(gòu)造函數(shù)的一種寫(xiě)法士葫,雖然合法但是比較草率:沒(méi)有使用構(gòu)造函數(shù)初始值
Sales_data::Sales_data(const string &s,unsigned cnt,double price) {
    bookNo = s;
    units_sold = cnt;
    revenue = cnt * price;
}
構(gòu)造函數(shù)的初始值有時(shí)必不可少

如果成員是const或者引用的話,必須將其初始化送悔。類(lèi)似的慢显,當(dāng)成員屬于某種類(lèi)類(lèi)型且該類(lèi)沒(méi)有定義默認(rèn)構(gòu)造函數(shù)時(shí),也必須將這個(gè)成員初始化欠啤。例如:

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

和其他常量對(duì)象或者引用一樣荚藻,成員ci和ri都必須被初始化。因此洁段,如果我們沒(méi)有為它們提供構(gòu)造函數(shù)初始值的話將引發(fā)錯(cuò)誤:

ConstRef::ConstRef(int ii) {
    //賦值
    i = ii; //正確
    ci = ii; //錯(cuò)誤:不能給const賦值
    ri = ii; //錯(cuò)誤:ri沒(méi)被初始化
}
//正確:顯示地初始化引用和const成員
ConstRef::ConstRef(int ii) :i(ii),ci(ii),ri(i) {}
成員初始化順序

成員的初始化順序與它們?cè)陬?lèi)定義中的出現(xiàn)順序一致:第一個(gè)成員先被初始化应狱,然后第二個(gè),依次類(lèi)推祠丝。構(gòu)造函數(shù)初始值列表中初始值的前后位置關(guān)系不會(huì)影響實(shí)際的初始化順序疾呻。

class X {
private:
    int i;
    int j;
public:
    //i的值為undefined,i在j之前初始化
    X(int val) :j(val), i(j) {}
};

==最好令構(gòu)造函數(shù)的初始值順序和成員聲明的順序保持一致。而且如果可能的話纽疟,盡量避免使用成員初始化其他成員。==

默認(rèn)實(shí)參和構(gòu)造函數(shù)

Sales_data默認(rèn)構(gòu)造函數(shù)的行為與只接受一個(gè)string實(shí)參的構(gòu)造函數(shù)差不多憾赁。唯一的區(qū)別是接受string實(shí)參的構(gòu)造函數(shù)使用這個(gè)實(shí)參初始化bookNo污朽,而默認(rèn)構(gòu)造函數(shù)使用string的默認(rèn)構(gòu)造函數(shù)初始化bookNo。

class Sales_data {
public:
    //定義默認(rèn)構(gòu)造函數(shù)龙考,令其與只接受一個(gè)string實(shí)參的構(gòu)造函數(shù)功能相同
    Sales_data(std::string s = ""):bookNo(s) {}
    Sales_data(std::string s,unsigned cnt,double rev):
    bookNo(s),units_sold(cnt),revenue(rev*cnt) {}
    Sales_data(std::istream &is) {
        read(is,*this);
    }
};

==如果一個(gè)構(gòu)造函數(shù)為所有參數(shù)都提供了默認(rèn)實(shí)參蟆肆,則它實(shí)際上也定義了默認(rèn)構(gòu)造函數(shù)。==

5.2 委托構(gòu)造函數(shù)

C++11新標(biāo)準(zhǔn)擴(kuò)展了構(gòu)造函數(shù)初始值的功能晦款,使得我們可以定義所謂的委托構(gòu)造函數(shù)炎功。
一個(gè)委托構(gòu)造函數(shù)使用它所屬類(lèi)的其他構(gòu)造函數(shù)執(zhí)行它自己的初始化過(guò)程,或者說(shuō)它把它自己的一些職責(zé)委托給了其他構(gòu)造函數(shù)缓溅。

class Sales_data {
public:
    //非委托構(gòu)造函數(shù)使用對(duì)應(yīng)的實(shí)參初始化成員
    Sales_data(std::string s,unsigned cnt,double price):
    bookNo(s),units_sold(cnt),revenue(cnt*price) {}
    //其余構(gòu)造函數(shù)全部委托給另一個(gè)構(gòu)造函數(shù)
    Sales_data():Sales_data("", 0, 0) {}
    Sales_data(std::string s):Sales_data(s,0,0) {}
    Sales_data(std::istream &is):Sales_data() {
        read(is,*this);
    }
};

5.3 默認(rèn)構(gòu)造函數(shù)的作用

當(dāng)對(duì)象被默認(rèn)初始化或值初始化時(shí)自動(dòng)執(zhí)行默認(rèn)構(gòu)造函數(shù)蛇损。默認(rèn)初始化在以下情況下發(fā)生:

  • 當(dāng)我們?cè)趬K作用域內(nèi)不使用任何初始值定義一個(gè)非靜態(tài)變量或數(shù)組時(shí)。
  • 當(dāng)一個(gè)類(lèi)本身含有類(lèi)類(lèi)型的成員且使用合成的默認(rèn)構(gòu)造函數(shù)時(shí)。
  • 當(dāng)類(lèi)類(lèi)型的成員沒(méi)有在構(gòu)造函數(shù)初始值列表顯式的初始化時(shí)淤齐。

值初始化在以下情況發(fā)生:

  • 在數(shù)組初始化的過(guò)程中如果我們提供的初始值數(shù)量少于數(shù)組的大小時(shí)股囊。
  • 當(dāng)我們不使用初始值定義一個(gè)局部靜態(tài)變量時(shí)。
  • 當(dāng)我們通過(guò)書(shū)寫(xiě)表達(dá)式顯示地請(qǐng)求值初始化時(shí)更啄,其中T是類(lèi)型名稚疹。
class NoDefault {
public:
    NoDefault(const std::string&);
};
struct A {
    NoDefault my_mem; //默認(rèn)情況下my_mem是public的
};
A a; //錯(cuò)誤:不能為A合成構(gòu)造函數(shù)
struct B {
    B(){} //錯(cuò)誤:b_member沒(méi)有初始值
    NoDefault b_member;
};

==在實(shí)際中,如果定義了其他構(gòu)造函數(shù)祭务,那么最好也提供一個(gè)默認(rèn)構(gòu)造函數(shù)内狗。==

使用默認(rèn)構(gòu)造函數(shù)
Sales_data obj(); //錯(cuò)誤:聲明一個(gè)函數(shù)而非對(duì)象
Sales_data obj2; //正確:obj2是一個(gè)對(duì)象而非函數(shù)

5.4 隱式的類(lèi)類(lèi)型轉(zhuǎn)換

如果構(gòu)造函數(shù)只接受一個(gè)實(shí)參,則它實(shí)際上定義了轉(zhuǎn)換為此類(lèi)類(lèi)型的隱式轉(zhuǎn)換機(jī)制义锥,有時(shí)我們把這種構(gòu)造函數(shù)稱(chēng)為轉(zhuǎn)換構(gòu)造函數(shù)柳沙。

只允許一步類(lèi)類(lèi)型轉(zhuǎn)換

編譯器只會(huì)自動(dòng)地執(zhí)行一步類(lèi)型轉(zhuǎn)換。例如缨该,因?yàn)橄旅娴拇a隱式地使用了兩種轉(zhuǎn)換規(guī)則偎行,所以它是錯(cuò)誤的:

//錯(cuò)誤:需要用戶(hù)定義的兩種轉(zhuǎn)換:
//(1)把“9-999-99999-9”轉(zhuǎn)換成string
//(2)再把這個(gè)臨時(shí)的string轉(zhuǎn)換成Sales_data
item.combine("9-999-99999-9");

如果想完成上述調(diào)用,可以顯示地把字符串轉(zhuǎn)換成string或者Sales_data對(duì)象:

//正確:顯示地轉(zhuǎn)換成string贰拿,隱式地轉(zhuǎn)換成Sales_data
item.combine(string("9-999-99999-9"));
//正確:隱式地轉(zhuǎn)換成string蛤袒,顯示地轉(zhuǎn)換成Sales_data
item.combine(Sales_data("9-999-99999-9"));
抑制構(gòu)造函數(shù)定義的隱式轉(zhuǎn)換

在要求隱式轉(zhuǎn)換的程序上下文中,我們可以通過(guò)將構(gòu)造函數(shù)聲明為explicit加以阻止:

class Sales_data {
public:
    Sales_data() = default;
    Sales_data(const std::string &s, unsigned n, double p) :
        bookNo(s), units_sold(n), revenue(p*n) {}
    explicit Sales_data(const std::string &s): bookNo(s) {}
    explicit Sales_data(std::istream &);
private:
    std::string bookNo;
    unsigned units_sold;
    double revenue;
};

此時(shí)膨更,沒(méi)有任何構(gòu)造函數(shù)能用于隱式地創(chuàng)建Sales_data對(duì)象妙真,之前的兩種用法都無(wú)法通過(guò)編譯:

item.combine(null_book); //錯(cuò)誤:string構(gòu)造函數(shù)是explicit的
item.combine(cin); //錯(cuò)誤:istream構(gòu)造函數(shù)是explicit的

關(guān)鍵字explicit只對(duì)一個(gè)實(shí)參的構(gòu)造函數(shù)有效。需要多個(gè)實(shí)參的構(gòu)造函數(shù)不能用于執(zhí)行隱式轉(zhuǎn)換荚守,所以無(wú)須將這些構(gòu)造函數(shù)指定為explicit的珍德。只能在類(lèi)內(nèi)聲明構(gòu)造函數(shù)時(shí)使用explicit關(guān)鍵字,在類(lèi)外部定義時(shí)不應(yīng)重復(fù):

//錯(cuò)誤:explicit關(guān)鍵字只允許出現(xiàn)在類(lèi)內(nèi)的構(gòu)造函數(shù)聲明處
explicit Sales_data::Sales_data(istream& is) {
    read(is,*this);
}
explicit構(gòu)造函數(shù)只能用于直接初始化

發(fā)生隱式轉(zhuǎn)換的一種情況是當(dāng)我們執(zhí)行拷貝形式的初始化時(shí)矗漾。此時(shí)锈候,我們只能使用直接初始化而不能使用explicit構(gòu)造函數(shù):

//正確:直接初始化
Sales_data item1(null_book);
//錯(cuò)誤:不能將explicit構(gòu)造函數(shù)用于拷貝形式的初始化過(guò)程
Sales_data item2 = null_book;

==當(dāng)我們用explicit關(guān)鍵字聲明構(gòu)造函數(shù)時(shí),它將只能以直接初始化的形式使用敞贡。而且泵琳,編譯器將不會(huì)在自動(dòng)轉(zhuǎn)換的過(guò)程中使用該構(gòu)造函數(shù)。==

為轉(zhuǎn)換顯示地使用構(gòu)造函數(shù)

盡管編譯器不會(huì)將explicit的構(gòu)造函數(shù)用于隱式轉(zhuǎn)換過(guò)程誊役,但是我們可以使用這樣的構(gòu)造函數(shù)顯示地強(qiáng)制進(jìn)行轉(zhuǎn)換:

//正確:實(shí)參是一個(gè)顯示構(gòu)造的Sales_data對(duì)象
item.combine(Sales_data(null_book));
//正確:static_cast可以使用explicit的構(gòu)造函數(shù)
item.combine(static_cast<Sales_data>(cin));

5.5 聚合類(lèi)

聚合類(lèi)使得用戶(hù)可以直接訪問(wèn)其成員获列,并且具有特殊的初始化語(yǔ)法形式。當(dāng)一個(gè)類(lèi)滿足如下條件時(shí)蛔垢,我們說(shuō)它是聚合的:

  • 所有成員都是public的击孩。
  • 沒(méi)有定義任何構(gòu)造函數(shù)。
  • 沒(méi)有類(lèi)內(nèi)初始值鹏漆。
  • 沒(méi)有基類(lèi)巩梢,也沒(méi)有virtual函數(shù)创泄。

例如,下面類(lèi)是一個(gè)聚合類(lèi):

strcuct Data {
    int ival;
    string s;
};

5.6 字面值常量類(lèi)

除了算術(shù)類(lèi)型且改、引用和指針外验烧,某些類(lèi)也是字面值類(lèi)型。和其他類(lèi)不同又跛,字面值類(lèi)型的類(lèi)可能含有constexpr函數(shù)成員碍拆。這樣的成員必須符合constexpr函數(shù)的所有要求,它們是隱式const的慨蓝。

數(shù)據(jù)成員都是字面值類(lèi)型的聚合類(lèi)是字面值常量類(lèi)感混。如果一個(gè)類(lèi)不是聚合類(lèi),但它符合下述要求礼烈,則它也是一個(gè)字面值常量類(lèi):

  • 數(shù)據(jù)成員都必須是字面值類(lèi)型弧满。
  • 類(lèi)必須含有一個(gè)constexpr構(gòu)造函數(shù)。
  • 如果一個(gè)數(shù)據(jù)成員含有類(lèi)內(nèi)初始值此熬,則內(nèi)置類(lèi)型成員的初始值必須是一條常量表達(dá)式庭呜;或者如果成員屬于某種類(lèi)類(lèi)型,則初始值必須使用成員自己的constexpr構(gòu)造函數(shù)犀忱。
  • 類(lèi)必須使用析構(gòu)函數(shù)的默認(rèn)定義募谎,該成員負(fù)責(zé)銷(xiāo)毀類(lèi)的對(duì)象。
constexpr構(gòu)造函數(shù)

盡管構(gòu)造函數(shù)不能是const的阴汇,但是字面值類(lèi)型的構(gòu)造函數(shù)可以是constexpr函數(shù)数冬。事實(shí)上,一個(gè)字面值常量類(lèi)必須至少提供一個(gè)constexpr構(gòu)造函數(shù)搀庶。

class Debug {
public:
    constexpr Debug(bool b = true) :hw(b),io(b),other(b) {}
    constexpr Debug(bool h,bool i,bool o) :
                        hw(h),io(i),other(o) {}
    constexpr bool any() {
        return hw||io||other;
    }
    void set_io(bool b) {
        io = b;
    }
    void set_hw(bool b) {
        hw = b;
    }
    void set_other(bool b) {
        hw = b;
    }
private:
    bool hw;
    bool io;
    bool other;
};

constexpr構(gòu)造函數(shù)必須初始化所有數(shù)據(jù)成員拐纱,初始值或者使用constexpr構(gòu)造函數(shù),或者是一條常量表達(dá)式哥倔。


#6. 類(lèi)的靜態(tài)成員

有的時(shí)候類(lèi)需要它的一些成員與類(lèi)本身直接相關(guān)秸架,而不是與類(lèi)的各個(gè)對(duì)象保持關(guān)聯(lián)。

聲明靜態(tài)成員

我們通過(guò)在成員的聲明之前加上關(guān)鍵字static使得其與類(lèi)關(guān)聯(lián)在一起咆蒿。和其他成員一樣东抹,靜態(tài)成員可以是public的或private的。靜態(tài)數(shù)據(jù)成員的類(lèi)型可以是常量蜡秽、引用府阀、指針缆镣、類(lèi)類(lèi)型等芽突。

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

類(lèi)的靜態(tài)成員存在于任何對(duì)象之外,對(duì)象中不包含任何與靜態(tài)數(shù)據(jù)成員有關(guān)的數(shù)據(jù)董瞻。

類(lèi)似的寞蚌,靜態(tài)成員函數(shù)也不與任何對(duì)象綁定在一起田巴,它們不包含this指針。作為結(jié)果挟秤,靜態(tài)成員函數(shù)不能聲明成const的壹哺,而且我們不能在static函數(shù)體內(nèi)使用this指針。這一限制即適用于this的顯式使用艘刚,也對(duì)調(diào)用非靜態(tài)成員的隱式使用有效管宵。

使用靜態(tài)成員

我們使用作用域運(yùn)算符直接訪問(wèn)靜態(tài)成員:

double r;
r = Account::rate(); //使用作用域運(yùn)算符訪問(wèn)靜態(tài)成員
定義靜態(tài)成員

和其他的成員函數(shù)一樣,我們既可以在類(lèi)的內(nèi)部也可以在內(nèi)的外部定義靜態(tài)成員函數(shù)攀甚。當(dāng)在類(lèi)的外部定義靜態(tài)成員時(shí)箩朴,不能重復(fù)static關(guān)鍵字,該關(guān)鍵字只出現(xiàn)在類(lèi)內(nèi)部的聲明語(yǔ)句:

void Account::rate(double newRate) {
    interestRate = newRate;
}

==和類(lèi)的所有成員一樣秋度,當(dāng)我們指向類(lèi)外部的靜態(tài)成員時(shí)炸庞,必須指明成員所屬的類(lèi)名。static關(guān)鍵字則只出現(xiàn)在類(lèi)內(nèi)部的聲明語(yǔ)句中荚斯。==

靜態(tài)成員的類(lèi)內(nèi)初始化

通常情況下埠居,類(lèi)的靜態(tài)成員不應(yīng)該在類(lèi)的內(nèi)部初始化。然而事期,我們可以為靜態(tài)成員提供const整數(shù)類(lèi)型的類(lèi)內(nèi)初始值滥壕,不過(guò)要求靜態(tài)成員必須是字面值常量類(lèi)型的constexpr。

class Account {
public:
    static double rate() {
        return interestRate;
    }
    static void rate(double);
private:
    static constexpr int period = 30; //period是常量表達(dá)式
    double daily_tbl[period];
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刑赶,一起剝皮案震驚了整個(gè)濱河市捏浊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撞叨,老刑警劉巖金踪,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異牵敷,居然都是意外死亡胡岔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)枷餐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)靶瘸,“玉大人,你說(shuō)我怎么就攤上這事毛肋≡惯洌” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵润匙,是天一觀的道長(zhǎng)诗眨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)孕讳,這世上最難降的妖魔是什么匠楚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任巍膘,我火速辦了婚禮,結(jié)果婚禮上芋簿,老公的妹妹穿的比我還像新娘峡懈。我一直安慰自己,他們只是感情好与斤,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布肪康。 她就那樣靜靜地躺著,像睡著了一般撩穿。 火紅的嫁衣襯著肌膚如雪梅鹦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天冗锁,我揣著相機(jī)與錄音齐唆,去河邊找鬼。 笑死冻河,一個(gè)胖子當(dāng)著我的面吹牛箍邮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播叨叙,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼锭弊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了擂错?” 一聲冷哼從身側(cè)響起味滞,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钮呀,沒(méi)想到半個(gè)月后剑鞍,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡爽醋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年蚁署,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚂四。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡光戈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出遂赠,到底是詐尸還是另有隱情久妆,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布跷睦,位于F島的核電站筷弦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏送讲。R本人自食惡果不足惜奸笤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哼鬓。 院中可真熱鬧监右,春花似錦、人聲如沸异希。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)称簿。三九已至扣癣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間憨降,已是汗流浹背父虑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留授药,地道東北人士嚎。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像悔叽,于是被迫代替她去往敵國(guó)和親莱衩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 使用類(lèi)定義自己的數(shù)據(jù)類(lèi)型 本章主要關(guān)注數(shù)據(jù)抽象→將對(duì)象的具體實(shí)現(xiàn)與對(duì)象所能執(zhí)行操作分離開(kāi)來(lái) 零娇澎、術(shù)語(yǔ)表 聚合類(lèi) 類(lèi)...
    菜雞也會(huì)飛閱讀 239評(píng)論 0 1
  • 數(shù)據(jù)抽象:是一種依賴(lài)于接口和實(shí)現(xiàn)分離的變成技術(shù)笨蚁。 封裝:分離接口(用戶(hù)所能執(zhí)行的操作)和實(shí)現(xiàn)(數(shù)據(jù)成員、實(shí)現(xiàn)接口的...
    咸魚(yú)翻身ing閱讀 201評(píng)論 0 0
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,511評(píng)論 1 51
  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔趟庄,今天18年5月份再次想寫(xiě)文章括细,發(fā)現(xiàn)簡(jiǎn)書(shū)還為我保存起的...
    Jenaral閱讀 2,737評(píng)論 2 9
  • 二十三兒 喝面葉兒 其實(shí)不是說(shuō)特別喜歡某種食物 像二十三的面葉兒,年三十的餃子戚啥,只是特定的時(shí)間吃特定的食物已經(jīng)成了...
    歡喜_b254閱讀 209評(píng)論 0 0