成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Cpp Primer | 第7章 : 類 (筆記+練習(xí))

testbird / 2748人閱讀

摘要:構(gòu)造函數(shù)的任務(wù)是初始化類對(duì)象的數(shù)據(jù)成員,無論何時(shí)只要類的對(duì)象被創(chuàng)建,就會(huì)執(zhí)行構(gòu)造函數(shù)。構(gòu)造函數(shù)名字和類名相同,沒有返回類型。構(gòu)造函數(shù)放在類的部分要求編譯器合成默認(rèn)的構(gòu)造函數(shù)。練習(xí)在你的類中添加構(gòu)造函數(shù),然后編寫一段程序令其用到每個(gè)構(gòu)造函數(shù)。

7.1 定義抽象數(shù)據(jù)類型

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

類成員

  • 必須在類的內(nèi)部聲明,不能在其他地方增加成員。
  • 成員可以是數(shù)據(jù),函數(shù),類型別名。

類的成員函數(shù)

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

偽代碼:說明隱式的this指針是如何使用的

下面的代碼是非法的:因?yàn)槲覀儾荒茱@示地定義自己的this指針, 在Sales_data成員函數(shù)中,this的類型是Sales_data *const

因?yàn)?strong>isbn是一個(gè)常量成員,此處的this是一個(gè)指向常量的指針

std::string Sales_data::isbn(const Sales_data *const this){	return this->isbn;}
  • 成員函數(shù)的聲明必須在類的內(nèi)部
  • 定義既可以在類的內(nèi)部也可以在類的外部(定義在類內(nèi)部的函數(shù)是隱式的)
  • 使用點(diǎn)運(yùn)算符.來訪問成員函數(shù)
  • 引入this
    • total.isbn()當(dāng)isbn返回bookNo時(shí),實(shí)際上它隱式的返回total.bookNo。成員函數(shù)通過一個(gè)名為this的額外的隱式參數(shù)來訪問調(diào)用它的那個(gè)對(duì)象。
    • 當(dāng)調(diào)用一個(gè)成員函數(shù)時(shí),用請(qǐng)求該函數(shù)的對(duì)象地址初始化this 偽代碼:Sales_data::isbn(&total)
    • 可以在成員函數(shù)體內(nèi)使用this,盡管沒有必要,但是能把isbn定義成:std::string isbn() const {return this->bookNo}
  • const作用:修改隱式this指針的類型。緊跟在參數(shù)列表后面的const表示this是一個(gè)指向常量的指針 —— 使用const的成員函數(shù)被稱為 常量成員函數(shù)。
    • const成員函數(shù):this是指向const類類型的const指針(既不能改變this所指向的值,也不能改變this保存的地址)。
    • 普通的非const成員函數(shù):this是指向類類型的const指針(可以改變this所指向的值,不能改變this保存的地址)。

非成員函數(shù)

  • 和類相關(guān)的非成員函數(shù),定義和聲明都應(yīng)該在類的外部。

  • 一般來說,如果非成員函數(shù)是類接口的組成部分,則這些函數(shù)的聲明應(yīng)該與類在同一個(gè)頭文件中。

類的構(gòu)造函數(shù)

  • 類通過一個(gè)或幾個(gè)特殊的成員函數(shù)來控制其對(duì)象的 初始化過程,這些函數(shù)叫做 構(gòu)造函數(shù)
  • 構(gòu)造函數(shù)的任務(wù)是初始化類對(duì)象的數(shù)據(jù)成員,無論何時(shí)只要類的對(duì)象被創(chuàng)建,就會(huì)執(zhí)行構(gòu)造函數(shù)。
  • 構(gòu)造函數(shù)名字和類名相同,沒有返回類型。
  • 構(gòu)造函數(shù)放在類的public部分
  • = default要求編譯器合成默認(rèn)的構(gòu)造函數(shù)。(C++11)
  • 初始化列表:冒號(hào)和花括號(hào)之間的代碼:Sales_item(): units_sold(0), revenue(0.0) { }、
  • IO屬于不能被拷貝的類型,因此只能通過引用來傳遞他們

7.2 訪問控制與封裝

  • 訪問說明符 —— 加強(qiáng)類的封裝性
    • public:定義在public后面的成員在整個(gè)程序內(nèi)可以被訪問;public成員定義類的接口
    • private:定義在private后面的成員可以被類的成員函數(shù)訪問,但不能被使用該類的代碼訪問;private隱藏了類的實(shí)現(xiàn)細(xì)節(jié)。
  • 使用classstruct:都可以被用于定義一個(gè)類,唯一的區(qū)別在于默認(rèn)訪問權(quán)限。
    • 使用class:在第一個(gè)訪問說明符之前的成員是private的。
    • 使用struct:在第一個(gè)訪問說明符之前的成員是public的。

友元

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

  • 允許特定的 非成員函數(shù)訪問一個(gè)類的私有成員。
  • 友元的聲明以關(guān)鍵字friend開始, friend Sales_data add(const Sales_data&, const Sales_data&);表示非成員函數(shù)add可以訪問類的非公有成員。
  • 通常將友元聲明成組地放在 類定義的開始或者結(jié)尾

封裝的益處

  • 確保用戶的代碼不會(huì)無意間破壞封裝對(duì)象的狀態(tài)。
  • 被封裝的類的具體實(shí)現(xiàn)細(xì)節(jié)可以隨時(shí)改變,而無需調(diào)整用戶級(jí)別的代碼。

7.3 類的其他特性

  • 成員函數(shù)作為內(nèi)聯(lián)函數(shù) inline
    • 在類的內(nèi)部,常有一些規(guī)模較小的函數(shù)適合于被聲明成內(nèi)聯(lián)函數(shù)。
    • 定義在類內(nèi)部的函數(shù)是自動(dòng)內(nèi)聯(lián)的。
    • 在類外部定義的成員函數(shù),也可以在聲明時(shí)顯式地加上 inline。
  • 可變成員函數(shù)(mutable data member)
    • 在變量的聲明中加入mutable關(guān)鍵字:mutable data access_ctr;
    • 可變數(shù)據(jù)成員永遠(yuǎn)不會(huì)是const,即使他是const對(duì)象的成員
  • 每個(gè)類確定了唯一的類型。對(duì)于兩個(gè)類來說,即使它們的成員完全一樣,這兩個(gè)類也是不同的類型。

7.4 類的作用域

  • 每個(gè)類都會(huì)定義自己的作用域。在類的作用域之外,普通的數(shù)據(jù)和函數(shù)成員只能由引用、對(duì)象、指針使用成員訪問運(yùn)算符來訪問。
  • 函數(shù)的 返回類型通常在函數(shù)名之前,因此當(dāng)成員函數(shù)定義在類的外部時(shí),返回類型中使用的名字都位于類的作用域之外,這時(shí),返回類型必須指明他是那個(gè)類的成員。
  • 如果成員使用了外層作用域中的某個(gè)名字,而該名字代表一種類型,則類不能在之后重新定義該名字。
  • 類中的類型名定義都要放在一開始。

7.5 構(gòu)造函數(shù)再探

構(gòu)造函數(shù)的初始化列表

  • 如果成員是 const或者是引用的話,必須將其初始化。只能初始化,不能賦值(注意初始化和賦值的區(qū)別)
    • 初始化:直接初始化數(shù)據(jù)成員
    • 賦值:先初始化再賦值
  • **最好令函數(shù)初始值的順序與成員聲明的順序保持一致。**構(gòu)造函數(shù)初始值只說明用于初始化成員的
  • 如果一個(gè)構(gòu)造函數(shù)為所有參數(shù)都提供了默認(rèn)參數(shù),那么它實(shí)際上也定義了默認(rèn)的構(gòu)造函數(shù)。

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

  • 委托構(gòu)造函數(shù)將自己的職責(zé)委托給了其他構(gòu)造函數(shù)
  • Sales_data(): Sales_data(" ", 0, 0) { }

只有當(dāng)一個(gè)類沒有定義 任何構(gòu)造函數(shù)的時(shí)候,編譯器才會(huì)生成一個(gè)默認(rèn)構(gòu)造函數(shù)。

隱式的類類型轉(zhuǎn)換(轉(zhuǎn)換構(gòu)造函數(shù))

  • 如果構(gòu)造函數(shù)只接受一個(gè)實(shí)參,則它實(shí)際上定義了轉(zhuǎn)換為此類類型的隱式轉(zhuǎn)換機(jī)制。這種構(gòu)造函數(shù)又叫轉(zhuǎn)換構(gòu)造函數(shù)(converting constructor)。

  • 能通過一個(gè)實(shí)參調(diào)用的構(gòu)造函數(shù)定義了一條從構(gòu)造函數(shù)的參數(shù)類型向類類型隱式轉(zhuǎn)換的規(guī)則。

  • 只允許 一步類類型轉(zhuǎn)換

  • 抑制構(gòu)造函數(shù)定義的隱式轉(zhuǎn)換

    • 將構(gòu)造函數(shù)聲明為 explicit加以阻止
    • explicit關(guān)鍵字只允許出現(xiàn)在類內(nèi)的構(gòu)造函數(shù)聲明處(只對(duì)一個(gè)實(shí)參的構(gòu)造函數(shù)有效)
    • explicit構(gòu)造函數(shù)只能用于直接初始化,不能將explicit構(gòu)造函數(shù)用于拷貝形式的初始化過程。
Sales_data item1(null_book);   //正確,直接初始化Sales_data item2 = null_book;  //錯(cuò)誤:不能將 explicit 構(gòu)造函數(shù)用于拷貝形式的初始化過程
  • 盡管編譯器不會(huì)將explicit的構(gòu)造函數(shù)用于隱式轉(zhuǎn)換過程,但是我們可以使用這樣的構(gòu)造函數(shù) 顯式地強(qiáng)制進(jìn)行轉(zhuǎn)換。

聚合類(aggregate class)

  • 聚合類使得用戶可以直接訪問其成員,并且具有特殊的初始化語法形式。

  • 滿足以下所有條件:

    • 所有成員都是 public
    • 沒有定義任何構(gòu)造函數(shù)
    • 沒有類內(nèi)初始值
    • 沒有基類,也沒有virtual函數(shù)
  • 可以使用一個(gè)花括號(hào)括起來的成員初始化列表,并用它初始化聚合類的數(shù)據(jù)成員。初始值的順序必須與聲明的順序一致。

字面值常量類

  • constexpr函數(shù)的參數(shù)和返回值必須是字面值類型
  • 字面值類型:除了算術(shù)類型、引用和指針外,某些類也是字面值類型
  • 數(shù)據(jù)成員都是字面值類型的聚合類是字面值常量類。
  • 如果不是聚合類,必須滿足下面所有條件
    • 數(shù)據(jù)成員都必須是字面值類型。
    • 類必須至少含有一個(gè)constexpr構(gòu)造函數(shù)。
    • 如果一個(gè)數(shù)據(jù)成員含有類內(nèi)部初始值,則內(nèi)置類型成員的初始值必須是一條常量表達(dá)式;或者如果成員屬于某種類類型,則初始值必須使用成員自己的constexpr構(gòu)造函數(shù)。
    • 類必須使用析構(gòu)函數(shù)的默認(rèn)定義,該成員負(fù)責(zé)銷毀類的對(duì)象。

7.6 類的靜態(tài)成員

  • static數(shù)據(jù)成員存在于類類型的每個(gè)對(duì)象中。
  • static數(shù)據(jù)成員獨(dú)立于該類的任意對(duì)象而存在。
  • 每個(gè)static數(shù)據(jù)成員是與類關(guān)聯(lián)的對(duì)象,并不與該類的對(duì)象相關(guān)聯(lián)。
  • 聲明:
    • 在聲明之前加上關(guān)鍵字static
  • 使用:
    • 使用 作用域運(yùn)算符 :: 直接訪問靜態(tài)成員 r = Account::rate()
    • 可以使用類的對(duì)象或引用訪問:r = ac1.rate()使用指針訪問:r = ac2->rate()
  • 定義:
    • 在類的外部定義靜態(tài)成員時(shí),不能重復(fù)static關(guān)鍵字,該關(guān)鍵字只出現(xiàn)在類內(nèi)部的聲明語句。
    • 在類外部定義時(shí)不用加static。
  • 初始化:
    • 通常不在類的內(nèi)部初始化,而是在定義時(shí)進(jìn)行初始化,如:double Account::interestRate = initRate();
    • 如果一定要在類內(nèi)定義,則要求必須是字面值常量類型的constexpr。

練習(xí)

練習(xí)7.1

使用2.6.1節(jié)定義的Sales_data類為1.6節(jié)的交易處理程序編寫一個(gè)新版本。

解:

#include #include using std::cin; using std::cout; using std::endl; using std::string;struct Sales_data{    string bookNo;    unsigned units_sold = 0;    double revenue = 0.0;};int main(){    Sales_data total;    if (cin >> total.bookNo >> total.units_sold >> total.revenue)    {        Sales_data trans;        while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue)         {            if (total.bookNo == trans.bookNo)             {                total.units_sold += trans.units_sold;                total.revenue += trans.revenue;            }            else            {                cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;                total = trans;            }        }        cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;    }    else    {        std::cerr << "No data?!" << std::endl;        return -1;    }    return 0;}

練習(xí)7.2

曾在2.6.2節(jié)的練習(xí)中編寫了一個(gè)Sales_data類,請(qǐng)向這個(gè)類添加combine函數(shù)和isbn成員。

解:

#include struct Sales_data {    std::string isbn() const { return bookNo; };    Sales_data& combine(const Sales_data&);        std::string bookNo;    unsigned units_sold = 0;    double revenue = 0.0;};Sales_data& Sales_data::combine(const Sales_data& rhs){    units_sold += rhs.units_sold;    revenue += rhs.revenue;    return *this;}

練習(xí)7.3

修改7.1.1節(jié)的交易處理程序,令其使用這些成員。

解:

#include using std::cin; using std::cout; using std::endl;int main(){    Sales_data total;    if (cin >> total.bookNo >> total.units_sold >> total.revenue)    {        Sales_data trans;        while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue) {            if (total.isbn() == trans.isbn())                total.combine(trans);            else {                cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;                total = trans;            }        }        cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;    }    else    {        std::cerr << "No data?!" << std::endl;        return -1;    }    return 0;}

練習(xí)7.4

編寫一個(gè)名為Person的類,使其表示人員的姓名和地址。使用string對(duì)象存放這些元素,接下來的練習(xí)將不斷充實(shí)這個(gè)類的其他特征。

解:

#include class Person {    std::string name;    std::string address;};

練習(xí)7.5

在你的Person類中提供一些操作使其能夠返回姓名和地址。 這些函數(shù)是否應(yīng)該是const的呢?解釋原因。

解:

#include class Person {    std::string name;    std::string address;public:    auto get_name() const -> std::string const& { return name; }    auto get_addr() const -> std::string const& { return address; }};

應(yīng)該是const的。因?yàn)槌A康?code>Person對(duì)象也需要使用這些函數(shù)操作。

練習(xí)7.6

對(duì)于函數(shù)addreadprint,定義你自己的版本。

解:

#include #include struct Sales_data {    std::string const& isbn() const { return bookNo; };    Sales_data& combine(const Sales_data&);    std::string bookNo;    unsigned units_sold = 0;    double revenue = 0.0;};// member functions.Sales_data& Sales_data::combine(const Sales_data& rhs){    units_sold += rhs.units_sold;    revenue += rhs.revenue;    return *this;}// nonmember functionsstd::istream &read(std::istream &is, Sales_data &item){    double price = 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;    return os;}Sales_data add(const Sales_data &lhs, const Sales_data &rhs){    Sales_data sum = lhs;    sum.combine(rhs);    return sum;}

練習(xí)7.7

使用這些新函數(shù)重寫7.1.2節(jié)練習(xí)中的程序。

int main(){    Sales_data total;    if (read(std::cin, total))    {        Sales_data trans;        while (read(std::cin, trans)) {            if (total.isbn() == trans.isbn())                total.combine(trans);            else {                print(std::cout, total) << std::endl;                total = trans;            }        }        print(std::cout, total) << std::endl;    }    else    {        std::cerr << "No data?!" << std::endl;        return -1;    }        return 0;}

練習(xí)7.8

為什么read函數(shù)將其Sales_data參數(shù)定義成普通的引用,而print函數(shù)將其參數(shù)定義成常量引用?

解:

因?yàn)?code>read函數(shù)會(huì)改變對(duì)象的內(nèi)容,而print函數(shù)不會(huì)。

練習(xí)7.9

對(duì)于7.1.2節(jié)練習(xí)中代碼,添加讀取和打印Person對(duì)象的操作。

#include #include struct Person {    std::string const& getName()    const { return name; }    std::string const& getAddress() const { return address; }        std::string name;    std::string address;};std::istream &read(std::istream &is, Person &person){    return is >> person.name >> person.address;}std::ostream &print(std::ostream &os, const Person &person){    return os << person.name << " " << person.address;}

練習(xí)7.10

在下面這條if語句中,條件部分的作用是什么?

if (read(read(cin, data1), data2)) //等價(jià)read(std::cin, data1);read(std::cin, data2);

解:

read函數(shù)的返回值是istream對(duì)象, if語句中條件部分的作用是從輸入流中讀取數(shù)據(jù)給兩個(gè)data對(duì)象。

練習(xí)7.11

在你的Sales_data類中添加構(gòu)造函數(shù), 然后編寫一段程序令其用到每個(gè)構(gòu)造函數(shù)。

解:

頭文件

#include #include struct Sales_data {    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(n*p){ }    Sales_data(std::istream &is);        std::string isbn() const { return bookNo; };    Sales_data& combine(const Sales_data&);        std::string bookNo;    unsigned units_sold = 0;    double revenue = 0.0;};// nonmember functionsstd::istream &read(std::istream &is, Sales_data &item){    double price = 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;    return os;}Sales_data add(const Sales_data &lhs, const Sales_data &rhs){    Sales_data sum = lhs;    sum.combine(rhs);    return sum;}// member functions.Sales_data::Sales_data(std::istream &is){    read(is, *this);}Sales_data& Sales_data::combine(const Sales_data& rhs){    units_sold += rhs.units_sold;    revenue += rhs.revenue;    return *this;}

主函數(shù)

int main(){    Sales_data item1;    print(std::cout, item1) << std::endl;        Sales_data item2("0-201-78345-X");    print(std::cout, item2) << std::endl;        Sales_data item3("0-201-78345-X", 3, 20.00);    print(std::cout, item3) << std::endl;        Sales_data item4(std::cin);    print(std::cout, item4) << std::endl;        return 0;}

練習(xí)7.12

把只接受一個(gè)istream作為參數(shù)的構(gòu)造函數(shù)移到類的內(nèi)部。

解:

#include #include struct Sales_data;std::istream &read(std::istream&, Sales_data&);struct Sales_data {    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(n*p){ }    Sales_data(std::istream &is) { read(is, *this); }        std::string isbn() const { return bookNo; };    Sales_data& combine(const Sales_data&);        std::string bookNo;    unsigned units_sold = 0;    double revenue = 0.0;};// member functions.Sales_data& Sales_data::combine(const Sales_data& rhs){    units_sold += rhs.units_sold;    revenue += rhs.revenue;    return *this;}// nonmember functionsstd::istream &read(std::istream &is, Sales_data &item){    double price = 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;    return os;}Sales_data add(const Sales_data &lhs, const Sales_data &rhs){    Sales_data sum = lhs;    sum.combine(rhs);    return sum;}

練習(xí)7.13

使用istream構(gòu)造函數(shù)重寫第229頁的程序。

int main(){    Sales_data total(std::cin);    if (!total.isbn().empty())    {        std::istream &is = std::cin;        while (is) {            Sales_data trans(is);            if (!is) break;            if (total.isbn() == trans.isbn())                total.combine(trans);            else {                print(std::cout, total) << std::endl;                total = trans;            }        }        print(std::cout, total) << std::endl;    }    else    {        std::cerr << "No data?!" << std::endl;        return -1;    }        return 0;}

練習(xí)7.14

編寫一個(gè)構(gòu)造函數(shù),令其用我們提供的類內(nèi)初始值顯式地初始化成員。

Sales_data() : units_sold(0) , revenue(0) { }

練習(xí)7.15

為你的Person類添加正確的構(gòu)造函數(shù)。

#include #include struct Person;std::istream &read(std::istream&, Person&);struct Person{	Person() = default;	Person(const std::string& sname, const std::string& saddr) :name(sname), address(saddr) {}	Person(std::istream &is) { read(is, *this); }	std::string getName() const { return name; }	std::string getAddress() const { return address; }	std::string name;	std::string address;};std::istream &read(std::istream &is, Person &person){	is >> person.name >> person.address;	return is;}std::ostream &print(std::ostream &os, const Person &person){	os << person.name << " " << person.address;	return os;}

練習(xí)7.16

在類的定義中對(duì)于訪問說明符出現(xiàn)的位置和次數(shù)有限定嗎? 如果有,是什么?什么樣的成員應(yīng)該定義在public說明符之后? 什么樣的成員應(yīng)該定義在private說明符之后?

解:

在類的定義中對(duì)于訪問說明符出現(xiàn)的位置和次數(shù)沒有限定

每個(gè)訪問說明符指定了接下來的成員的訪問級(jí)別,其有效范圍直到出現(xiàn)下一個(gè)訪問說明符或者達(dá)到類的結(jié)尾處為止。

如果某個(gè)成員能夠在整個(gè)程序內(nèi)都被訪問,那么它應(yīng)該定義為public; 如果某個(gè)成員只能在類內(nèi)部訪問,那么它應(yīng)該定義為private

練習(xí)7.17

使用classstruct時(shí)有區(qū)別嗎?如果有,是什么?

解:

classstruct的唯一區(qū)別是默認(rèn)的訪問級(jí)別不同。

練習(xí)7.18

封裝是何含義?它有什么用處?

將類內(nèi)部分成員設(shè)置為外部不可見,而提供部分接口給外面,這樣的行為叫做封裝

用處:

  • 1.確保用戶的代碼不會(huì)無意間破壞封裝對(duì)象的狀態(tài)。
  • 2.被封裝的類的具體實(shí)現(xiàn)細(xì)節(jié)可以隨時(shí)改變,而無需調(diào)整用戶級(jí)別的代碼。

練習(xí)7.19

在你的Person類中,你將把哪些成員聲明成public的? 哪些聲明成private的? 解釋你這樣做的原因。

構(gòu)造函數(shù)、getName()、getAddress()函數(shù)將設(shè)為public。 nameaddress 將設(shè)為private。 函數(shù)是暴露給外部的接口,因此要設(shè)為public; 而數(shù)據(jù)則應(yīng)該隱藏讓外部不可見。

練習(xí)7.21

修改你的Sales_data類使其隱藏實(shí)現(xiàn)的細(xì)節(jié)。 你之前編寫的關(guān)于Sales_data操作的程序應(yīng)該繼續(xù)使用,借助類的新定義重新編譯該程序,確保其正常工作。

#include #include class Sales_data {    friend std::istream &read(std::istream &is, Sales_data &item);    friend std::ostream &print(std::ostream &os, const Sales_data &item);    friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);public:    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(n*p){ }    Sales_data(std::istream &is) { read(is, *this); }    std::string isbn() const { return bookNo; };    Sales_data& combine(const Sales_data&);private:    std::string bookNo;    unsigned units_sold = 0;    double revenue = 0.0;};// member functions.Sales_data& Sales_data::combine(const Sales_data& rhs){    units_sold += rhs.units_sold;    revenue += rhs.revenue;    return *this;}// friend functionsstd::istream &read(std::istream &is, Sales_data &item){    double price = 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;    return os;}Sales_data add(const Sales_data &lhs, const Sales_data &rhs){    Sales_data sum = lhs;    sum.combine(rhs);    return sum;}

練習(xí)7.22

修改你的Person類使其隱藏實(shí)現(xiàn)的細(xì)節(jié)。

#include #include class Person {    friend std::istream &read(std::istream &is, Person &person);    friend std::ostream &print(std::ostream &os, const Person &person);public:    Person() = default;    Person(const std::string sname, const std::string saddr):name(sname), address(saddr){ }    Person(std::istream &is){ read(is, *this); }    std::string getName() const { return name; }    std::string getAddress() const { return address; }private:    std::string name;    std::string address;};std::istream &read(std::istream &is, Person &person){    is >> person.name >> person.address;    return is;}std::ostream &print(std::ostream &os, const Person &person){    os << person.name << " " << person.address;    return os;}

練習(xí)7.23

編寫你自己的Screen類型。

#include class Screen {    public:        using pos = std::string::size_type;        Screen() = default;        Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ }        char get() const { return contents[cursor]; }        char get(pos r, pos c) const { return contents[r*width+c]; }    private:        pos cursor = 0;        pos height = 0, width = 0;        std::string contents;};

練習(xí)7.24

給你的Screen類添加三個(gè)構(gòu)造函數(shù):一個(gè)默認(rèn)構(gòu)造函數(shù);另一個(gè)構(gòu)造函數(shù)接受寬和高的值,然后將contents初始化成給定數(shù)量的空白;第三個(gè)構(gòu)造函數(shù)接受寬和高的值以及一個(gè)字符,該字符作為初始化后屏幕的內(nèi)容。

#include class Screen {    public:        using pos = std::string::size_type;        Screen() = default; // 1        Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, " "){ } // 2        Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } // 3        char get() const { return contents[cursor]; }        char get(pos r, pos c) const { return contents[r*width+c]; }    private:        pos cursor = 0;        pos height = 0, width = 0;        std::string contents;};

練習(xí)7.27

給你自己的Screen類添加move、setdisplay函數(shù),通過執(zhí)行下面的代碼檢驗(yàn)?zāi)愕念愂欠裾_。

Screen myScreen(5, 5, "X");myScreen.move(4, 0).set("#").display(cout);cout << "/n";myScreen.display(cout);cout << "/n";

解:

增加代碼:

#include #include class Screen {public:    ... ...    inline Screen& move(pos r, pos c);    inline Screen& set(char c);    inline Screen& set(pos r, pos c, char ch);    const Screen& display(std::ostream &os) const { do_display(os); return *this; }    Screen& display(std::ostream &os) { do_display(os); return *this; }private:    void do_display(std::ostream &os) const { os << contents; }    ... ...};inline Screen& Screen::move(pos r, pos c){    cursor = r*width + c;    return *this;}inline Screen& Screen::set(char c){    contents[cursor] = c;    return *this;}inline Screen& Screen::set(pos r, pos c, char ch){    contents[r*width+c] = ch;    return *this;}

測試代碼:

int main(){    Screen myScreen(5, 5, "X");    myScreen.move(4, 0).set("#").display(std::cout);    std::cout << "/n";    myScreen.display(std::cout);    std::cout << "/n";    return 0;}

練習(xí)7.28

如果move、setdisplay函數(shù)的返回類型不是Screen& 而是Screen,則在上一個(gè)練習(xí)中將會(huì)發(fā)生什么?

解:

如果返回類型是Screen,那么move返回的是*this的一個(gè)副本,因此set函數(shù)只能改變臨時(shí)副本而不能改變myScreen的值。

練習(xí)7.29

修改你的Screen類,令move、setdisplay函數(shù)返回Screen并檢查程序的運(yùn)行結(jié)果,在上一個(gè)練習(xí)中你的推測正確嗎?

解:

推測正確。

#with "&"XXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXX#XXXX                    ^# without "&"XXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXX                    ^

練習(xí)7.31

定義一對(duì)類XY,其中X包含一個(gè)指向Y的指針,而Y包含一個(gè)類型為X的對(duì)象。

class Y;class X {	Y* y = nullptr;};class Y {	X x;};

練習(xí)7.32

定義你自己的ScreenWindow_mgr,其中clearWindow_mgr的成員,是Screen的友元。

#include #include #include class Screen;class Window_mgr {public:	using ScreenIndex = std::vector::size_type;	//按照編號(hào)將指定的Screen置為空白	void clear(ScreenIndex); private:	std::vector screen;};class Screen {	friend void Window_mgr::clear(ScreenIndex);public:	using pos = std::string::size_type;	//構(gòu)造函數(shù)	Screen() = default;	Screen(pos ht, pos wd):height(ht),width(wd),contents(ht*wd," "){ }	Screen(pos ht, pos wd, char c): height(ht),width(wd),contents(ht*wd,c){ }		inline Screen& move(pos r, pos c);	inline Screen& set(char c);	inline Screen& set(pos r, pos c, char ch);	const Screen& display(std::ostream& os) const { do_display(os); return *this; }	Screen& display(std::ostream& os) { do_display(os); return *this; }private:	pos cursor = 0;	pos height = 0, width = 0;	std::string contents;	void do_display(std::ostream& os) const { os << contents; }};inline void Window_mgr::clear(ScreenIndex i){	Screen& s = screen[i];	s.contents = std::string(s.height * s.width, " ");}inline Screen& Screen::move(pos r, pos c){	cursor = r * width + c;	return *this;}inline Screen& Screen::set(char c){	contents[cursor] = c;	return *this;}inline Screen& Screen::set(pos r, pos c, char ch){	contents[r * width + c] = ch;	return *this;}

練習(xí)7.34

如果我們把第256頁Screen類的postypedef放在類的最后一行會(huì)發(fā)生什么情況?

解:

在 dummy_fcn(pos height) 函數(shù)中會(huì)出現(xiàn) 未定義的標(biāo)識(shí)符pos。

類型名的定義通常出現(xiàn)在類的開始處,這樣就能確保所有使用該類型的成員都出現(xiàn)在類名的定義之后。

練習(xí)7.38

有些情況下我們希望提供cin作為接受istream&參數(shù)的構(gòu)造函數(shù)的默認(rèn)實(shí)參,請(qǐng)聲明這樣的構(gòu)造函數(shù)。

解:

Sales_data(std::istream &is = std::cin) { read(is, *this); }

練習(xí)7.41

使用委托構(gòu)造函數(shù)重新編寫你的Sales_data類,給每個(gè)構(gòu)造函數(shù)體添加一條語句,令其一旦執(zhí)行就打印一條信息。用各種可能的方式分別創(chuàng)建Sales_data對(duì)象,認(rèn)真研究每次輸出的信息直到你確實(shí)理解了委托構(gòu)造函數(shù)的執(zhí)行順序。

頭文件

#ifndef CP5_ex7_41_h#define CP5_ex7_41_h#include #include class Sales_data {    friend std::istream &read(std::istream &is, Sales_data &item);    friend std::ostream &print(std::ostream &os, const Sales_data &item);    friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);public:    Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p)    { std::cout << "Sales_data(const std::string&, unsigned, double)" << std::endl; }        Sales_data() : Sales_data("", 0, 0.0f)    { std::cout << "Sales_data()" << std::endl; }        Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f)    { std::cout << "Sales_data(const std::string&)" << std::endl; }        Sales_data(std::istream &is);    std::string isbn() const { return bookNo; }    Sales_data& combine(const Sales_data&);    private:    inline double avg_price() const;        private:    std::string bookNo;    unsigned units_sold = 0;    double revenue = 0.0;};inlinedouble Sales_data::avg_price() const{    return units_sold ? revenue/units_sold : 0;}// declarations for nonmember parts of the Sales_data interface.std::istream &read(std::istream &is, Sales_data &item);std::ostream &print(std::ostream &os, const Sales_data &item);Sales_data add(const Sales_data &lhs, const Sales_data &rhs);#endif

源文件

#include "ex_7_41.h"http:// constructorSales_data::Sales_data(std::istream &is) : Sales_data(){    std::cout << "Sales_data(istream &is)" << std::endl;    read(is, *this);}// member functions.Sales_data& Sales_data::combine(const Sales_data& rhs){    units_sold += rhs.units_sold;    revenue += rhs.revenue;    return *this;}// friend functionsstd::istream &read(std::istream &is, Sales_data &item){    double price = 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;    return os;}Sales_data add(const Sales_data &lhs, const Sales_data &rhs){    Sales_data sum = lhs;    sum.combine(rhs);    return sum;}

主函數(shù)

#include "ex_7_41.h"using std::cout; using std::endl;int main(){    cout << "1. default way: " << endl;    cout << "----------------" << endl;    Sales_data s1;           cout << "/n2. use std::string as parameter: " << endl;    cout << "----------------" << endl;    Sales_data s2("CPP-Primer-5th");        cout << "/n3. complete parameters: " << endl;    cout << "----------------" << endl;    Sales_data s3("CPP-Primer-5th", 3, 25.8);        cout << "/n4. use istream as parameter: " << endl;    cout << "----------------" << endl;    Sales_data s4(std::cin);        return 0;}

輸出

1. default way:----------------Sales_data(const string& s, unsigned n, double p)Sales_data()2. use std::string as parameter:----------------Sales_data(const string& s, unsigned n, double p)Sales_data(const string& s)3. complete parameters:----------------Sales_data(const string& s, unsigned n, double p)4. use istream as parameter:----------------Sales_data(const string& s, unsigned n, double p)Sales_data()Sales_data(istream& is)

總結(jié):使用委托構(gòu)造函數(shù),調(diào)用順序是:

  • 1.實(shí)際的構(gòu)造函數(shù)的函數(shù)體。
  • 2.委托構(gòu)造函數(shù)的函數(shù)體。

練習(xí)7.43

假定有一個(gè)名為NoDefault的類,它有一個(gè)接受int的構(gòu)造函數(shù),但是沒有默認(rèn)構(gòu)造函數(shù)。定義類C,C有一個(gè) NoDefault類型的成員,定義C的默認(rèn)構(gòu)造函數(shù)。

class NoDefault {public:    NoDefault(int i) { }};class C {public:    C() : def(0) { } private:    NoDefault def;};

練習(xí)7.47

說明接受一個(gè)string參數(shù)的Sales_data構(gòu)造函數(shù)是否應(yīng)該是explicit的,并解釋這樣做的優(yōu)缺點(diǎn)。

解:

是否需要從stringSales_data的轉(zhuǎn)換依賴于我們對(duì)用戶使用該轉(zhuǎn)換的看法。在此例中,這種轉(zhuǎn)換可能是對(duì)的。null_book中的string可能表示了一個(gè)不存在的ISBN編號(hào)。

優(yōu)點(diǎn):

可以抑制構(gòu)造函數(shù)定義的隱式轉(zhuǎn)換

缺點(diǎn):

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

練習(xí)7.48

假定Sales_data的構(gòu)造函數(shù)不是explicit的,則下述定義將執(zhí)行什么樣的操作?

解:

string null_isbn("9-999-9999-9");Sales_data item1(null_isbn);Sales_data item2("9-999-99999-9");

這些定義和是不是explicit的無關(guān)。

練習(xí)7.49

對(duì)于combine函數(shù)的三種不同聲明,當(dāng)我們調(diào)用i.combine(s)時(shí)分別發(fā)生什么情況?其中i是一個(gè)Sales_data,而s是一個(gè)string對(duì)象。

解:

(a) Sales_data &combine(Sales_data); // ok(b) Sales_data &combine(Sales_data&); // error C2664: 無法將參數(shù) 1 從“std::string”轉(zhuǎn)換為“Sales_data &”	因?yàn)殡[式轉(zhuǎn)換只有一次(c) Sales_data &combine(const Sales_data&) const; // 該成員函數(shù)是const 的,意味著不能改變對(duì)象。而 combine函數(shù)的本意就是要改變對(duì)象

練習(xí)7.50

確定在你的Person類中是否有一些構(gòu)造函數(shù)應(yīng)該是explicit 的。

explicit Person(std::istream& is) { read(is, *this); }

練習(xí)7.51

vector將其單參數(shù)的構(gòu)造函數(shù)定義成explicit的,而string則不是,你覺得原因何在?

假如我們有一個(gè)這樣的函數(shù):

int getSize(const std::vector&);

如果vector沒有將單參數(shù)構(gòu)造函數(shù)定義成explicit的,我們就可以這樣調(diào)用:

getSize(34);

很明顯這樣調(diào)用會(huì)讓人困惑,函數(shù)實(shí)際上會(huì)初始化一個(gè)擁有34個(gè)元素的vector的臨時(shí)量,然后返回34。但是這樣沒有任何意義。而string則不同,string的單參數(shù)構(gòu)造函數(shù)的參數(shù)是const char *,因此凡是在需要用到string的地方都可以用const char *來代替(字面值就是const char *)。如:

void print(std::string);print("hello world");

練習(xí)7.52

使用2.6.1節(jié)的 Sales_data 類,解釋下面的初始化過程。如果存在問題,嘗試修改它。

Sales_data item = {"987-0590353403", 25, 15.99};

解:

Sales_data 類不是聚合類,應(yīng)該修改成如下:

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

練習(xí)7.53

定義你自己的Debug

解:

class Debug {public:    constexpr Debug(bool b = true) : hw(b), io(b), other(b) { }    constexpr Debug(bool h, bool i, bool o) : hw(r), io(i), other(0) { }    constexpr bool any() { return hw || io || other; }    void set_hw(bool b) { hw = b; }    void set_io(bool b) { io = b; }    void set_other(bool b) { other = b; }    private:    bool hw;        // runtime error    bool io;        // I/O error    bool other;     // the others};

練習(xí)7.54

Debug中以 set_ 開頭的成員應(yīng)該被聲明成constexpr 嗎?如果不,為什么?

解:

不能。constexpr函數(shù)必須包含一個(gè)返回語句

練習(xí)7.55

7.5.5節(jié)的Data類是字面值常量類嗎?請(qǐng)解釋原因。

解:

不是。因?yàn)?code>std::string不是字面值類型。

練習(xí)7.57

編寫你自己的Account類。

解:

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

練習(xí)7.58

下面的靜態(tài)數(shù)據(jù)成員的聲明和定義有錯(cuò)誤嗎?請(qǐng)解釋原因。

//example.hclass Example {public:	static double rate = 6.5;	static const int vecSize = 20;	static vector vec(vecSize);};//example.c#include "example.h"double Example::rate;vector Example::vec;

解:

rate應(yīng)該是一個(gè)常量表達(dá)式。而類內(nèi)只能初始化整型類型的靜態(tài)常量,所以不能在類內(nèi)初始化vec。修改后如下:

// example.hclass Example {public:    static constexpr double rate = 6.5;    static const int vecSize = 20;    static vector vec;};// example.C#include "example.h"constexpr double Example::rate;vector Example::vec(Example::vecSize);

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/119304.html

相關(guān)文章

  • 系統(tǒng)地學(xué)習(xí)C++

    摘要:本書主要圍繞一系列逐漸復(fù)雜的程序問題,以及用以解決這些問題的語言特性展開講解。你不只學(xué)到的函數(shù)和結(jié)構(gòu),也會(huì)學(xué)習(xí)到它們的設(shè)計(jì)目的和基本原理。因此我們把精力集中在最有價(jià)值的地方。本書不僅是對(duì)模板的權(quán)威解釋,而且本書還深入地介紹了其他一般的思想。 C++ 入門教程(41課時(shí)) - 阿里云大學(xué) C+...

    joyqi 評(píng)論0 收藏0
  • python怎么實(shí)現(xiàn)自動(dòng)生成C++代碼

      小編寫這篇文章,主要目的還是給大家講一下關(guān)于python代碼的相關(guān)事宜,比如怎么才能夠?qū)崿F(xiàn)自動(dòng)生產(chǎn)C++代碼,這里面還是比較的復(fù)雜的,下面小編就給大家貼出具體的代碼給大家來看下?! ∮龅降膯栴}  工作中遇到這么一個(gè)事,需要寫比較多的C++的底層數(shù)據(jù)庫類,但這些類大同小異,無非是增刪改查,如果人工來寫代碼,既費(fèi)力又容易出錯(cuò);而借用python的代碼自動(dòng)生成,可以輕松搞定; ?。惐菾AVA中的H...

    89542767 評(píng)論0 收藏0
  • ApacheCN 人工智能知識(shí)樹 v1.0

    摘要:貢獻(xiàn)者飛龍版本最近總是有人問我,把這些資料看完一遍要用多長時(shí)間,如果你一本書一本書看的話,的確要用很長時(shí)間。為了方便大家,我就把每本書的章節(jié)拆開,再按照知識(shí)點(diǎn)合并,手動(dòng)整理了這個(gè)知識(shí)樹。 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1760&h=200); 貢獻(xiàn)者:飛龍版...

    劉厚水 評(píng)論0 收藏0
  • python協(xié)程1:yield 10分鐘入門

    摘要:協(xié)程定義協(xié)程的底層架構(gòu)是在中定義,并在實(shí)現(xiàn)的。為了簡化,我們會(huì)使用裝飾器預(yù)激協(xié)程。執(zhí)行上述代碼結(jié)果如下出錯(cuò)的原因是發(fā)送給協(xié)程的值不能加到變量上。示例使用和方法控制協(xié)程。 最近找到一本python好書《流暢的python》,是到現(xiàn)在為止看到的對(duì)python高級(jí)特性講述最詳細(xì)的一本。看了協(xié)程一章,做個(gè)讀書筆記,加深印象。 協(xié)程定義 協(xié)程的底層架構(gòu)是在pep342 中定義,并在python2...

    MartinDai 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<