国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

testbird / 2725人閱讀

摘要:構造函數的任務是初始化類對象的數據成員,無論何時只要類的對象被創建,就會執行構造函數。構造函數名字和類名相同,沒有返回類型。構造函數放在類的部分要求編譯器合成默認的構造函數。練習在你的類中添加構造函數,然后編寫一段程序令其用到每個構造函數。

7.1 定義抽象數據類型

  • 類的基本思想:數據抽象 和 封裝。
  • 數據抽象是一種依賴于接口和實現分離的編程技術。

類成員

  • 必須在類的內部聲明,不能在其他地方增加成員。
  • 成員可以是數據,函數,類型別名。

類的成員函數

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

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

下面的代碼是非法的:因為我們不能顯示地定義自己的this指針, 在Sales_data成員函數中,this的類型是Sales_data *const

因為isbn是一個常量成員,此處的this是一個指向常量的指針

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

非成員函數

  • 和類相關的非成員函數,定義和聲明都應該在類的外部。

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

類的構造函數

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

7.2 訪問控制與封裝

  • 訪問說明符 —— 加強類的封裝性
    • public:定義在public后面的成員在整個程序內可以被訪問;public成員定義類的接口
    • private:定義在private后面的成員可以被類的成員函數訪問,但不能被使用該類的代碼訪問;private隱藏了類的實現細節。
  • 使用classstruct:都可以被用于定義一個類,唯一的區別在于默認訪問權限。
    • 使用class:在第一個訪問說明符之前的成員是private的。
    • 使用struct:在第一個訪問說明符之前的成員是public的。

友元

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

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

封裝的益處

  • 確保用戶的代碼不會無意間破壞封裝對象的狀態。
  • 被封裝的類的具體實現細節可以隨時改變,而無需調整用戶級別的代碼。

7.3 類的其他特性

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

7.4 類的作用域

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

7.5 構造函數再探

構造函數的初始化列表

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

委托構造函數

  • 委托構造函數將自己的職責委托給了其他構造函數
  • Sales_data(): Sales_data(" ", 0, 0) { }

只有當一個類沒有定義 任何構造函數的時候,編譯器才會生成一個默認構造函數。

隱式的類類型轉換(轉換構造函數)

  • 如果構造函數只接受一個實參,則它實際上定義了轉換為此類類型的隱式轉換機制。這種構造函數又叫轉換構造函數(converting constructor)。

  • 能通過一個實參調用的構造函數定義了一條從構造函數的參數類型向類類型隱式轉換的規則。

  • 只允許 一步類類型轉換

  • 抑制構造函數定義的隱式轉換

    • 將構造函數聲明為 explicit加以阻止
    • explicit關鍵字只允許出現在類內的構造函數聲明處(只對一個實參的構造函數有效)
    • explicit構造函數只能用于直接初始化,不能將explicit構造函數用于拷貝形式的初始化過程。
Sales_data item1(null_book);   //正確,直接初始化Sales_data item2 = null_book;  //錯誤:不能將 explicit 構造函數用于拷貝形式的初始化過程
  • 盡管編譯器不會將explicit的構造函數用于隱式轉換過程,但是我們可以使用這樣的構造函數 顯式地強制進行轉換。

聚合類(aggregate class)

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

  • 滿足以下所有條件:

    • 所有成員都是 public
    • 沒有定義任何構造函數
    • 沒有類內初始值
    • 沒有基類,也沒有virtual函數
  • 可以使用一個花括號括起來的成員初始化列表,并用它初始化聚合類的數據成員。初始值的順序必須與聲明的順序一致。

字面值常量類

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

7.6 類的靜態成員

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

練習

練習7.1

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

解:

#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;}

練習7.2

曾在2.6.2節的練習中編寫了一個Sales_data類,請向這個類添加combine函數和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;}

練習7.3

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

解:

#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;}

練習7.4

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

解:

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

練習7.5

在你的Person類中提供一些操作使其能夠返回姓名和地址。 這些函數是否應該是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; }};

應該是const的。因為常量的Person對象也需要使用這些函數操作。

練習7.6

對于函數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;}

練習7.7

使用這些新函數重寫7.1.2節練習中的程序。

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;}

練習7.8

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

解:

因為read函數會改變對象的內容,而print函數不會。

練習7.9

對于7.1.2節練習中代碼,添加讀取和打印Person對象的操作。

#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;}

練習7.10

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

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

解:

read函數的返回值是istream對象, if語句中條件部分的作用是從輸入流中讀取數據給兩個data對象。

練習7.11

在你的Sales_data類中添加構造函數, 然后編寫一段程序令其用到每個構造函數。

解:

頭文件

#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;}

主函數

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;}

練習7.12

把只接受一個istream作為參數的構造函數移到類的內部。

解:

#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;}

練習7.13

使用istream構造函數重寫第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;}

練習7.14

編寫一個構造函數,令其用我們提供的類內初始值顯式地初始化成員。

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

練習7.15

為你的Person類添加正確的構造函數。

#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;}

練習7.16

在類的定義中對于訪問說明符出現的位置和次數有限定嗎? 如果有,是什么?什么樣的成員應該定義在public說明符之后? 什么樣的成員應該定義在private說明符之后?

解:

在類的定義中對于訪問說明符出現的位置和次數沒有限定

每個訪問說明符指定了接下來的成員的訪問級別,其有效范圍直到出現下一個訪問說明符或者達到類的結尾處為止。

如果某個成員能夠在整個程序內都被訪問,那么它應該定義為public; 如果某個成員只能在類內部訪問,那么它應該定義為private

練習7.17

使用classstruct時有區別嗎?如果有,是什么?

解:

classstruct的唯一區別是默認的訪問級別不同。

練習7.18

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

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

用處:

  • 1.確保用戶的代碼不會無意間破壞封裝對象的狀態。
  • 2.被封裝的類的具體實現細節可以隨時改變,而無需調整用戶級別的代碼。

練習7.19

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

構造函數、getName()getAddress()函數將設為publicnameaddress 將設為private。 函數是暴露給外部的接口,因此要設為public; 而數據則應該隱藏讓外部不可見。

練習7.21

修改你的Sales_data類使其隱藏實現的細節。 你之前編寫的關于Sales_data操作的程序應該繼續使用,借助類的新定義重新編譯該程序,確保其正常工作。

#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;}

練習7.22

修改你的Person類使其隱藏實現的細節。

#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;}

練習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;};

練習7.24

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

#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;};

練習7.27

給你自己的Screen類添加movesetdisplay函數,通過執行下面的代碼檢驗你的類是否正確。

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;}

練習7.28

如果movesetdisplay函數的返回類型不是Screen& 而是Screen,則在上一個練習中將會發生什么?

解:

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

練習7.29

修改你的Screen類,令movesetdisplay函數返回Screen并檢查程序的運行結果,在上一個練習中你的推測正確嗎?

解:

推測正確。

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

練習7.31

定義一對類XY,其中X包含一個指向Y的指針,而Y包含一個類型為X的對象。

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

練習7.32

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

#include #include #include class Screen;class Window_mgr {public:	using ScreenIndex = std::vector::size_type;	//按照編號將指定的Screen置為空白	void clear(ScreenIndex); private:	std::vector screen;};class Screen {	friend void Window_mgr::clear(ScreenIndex);public:	using pos = std::string::size_type;	//構造函數	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;}

練習7.34

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

解:

在 dummy_fcn(pos height) 函數中會出現 未定義的標識符pos。

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

練習7.38

有些情況下我們希望提供cin作為接受istream&參數的構造函數的默認實參,請聲明這樣的構造函數。

解:

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

練習7.41

使用委托構造函數重新編寫你的Sales_data類,給每個構造函數體添加一條語句,令其一旦執行就打印一條信息。用各種可能的方式分別創建Sales_data對象,認真研究每次輸出的信息直到你確實理解了委托構造函數的執行順序。

頭文件

#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;}

主函數

#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)

總結:使用委托構造函數,調用順序是:

  • 1.實際的構造函數的函數體。
  • 2.委托構造函數的函數體。

練習7.43

假定有一個名為NoDefault的類,它有一個接受int的構造函數,但是沒有默認構造函數。定義類CC有一個 NoDefault類型的成員,定義C的默認構造函數。

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

練習7.47

說明接受一個string參數的Sales_data構造函數是否應該是explicit的,并解釋這樣做的優缺點。

解:

是否需要從stringSales_data的轉換依賴于我們對用戶使用該轉換的看法。在此例中,這種轉換可能是對的。null_book中的string可能表示了一個不存在的ISBN編號。

優點:

可以抑制構造函數定義的隱式轉換

缺點:

為了轉換要顯式地使用構造函數

練習7.48

假定Sales_data的構造函數不是explicit的,則下述定義將執行什么樣的操作?

解:

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

這些定義和是不是explicit的無關。

練習7.49

對于combine函數的三種不同聲明,當我們調用i.combine(s)時分別發生什么情況?其中i是一個Sales_data,而s是一個string對象。

解:

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

練習7.50

確定在你的Person類中是否有一些構造函數應該是explicit 的。

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

練習7.51

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

假如我們有一個這樣的函數:

int getSize(const std::vector&);

如果vector沒有將單參數構造函數定義成explicit的,我們就可以這樣調用:

getSize(34);

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

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

練習7.52

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

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

解:

Sales_data 類不是聚合類,應該修改成如下:

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

練習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};

練習7.54

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

解:

不能。constexpr函數必須包含一個返回語句

練習7.55

7.5.5節的Data類是字面值常量類嗎?請解釋原因。

解:

不是。因為std::string不是字面值類型。

練習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();

練習7.58

下面的靜態數據成員的聲明和定義有錯誤嗎?請解釋原因。

//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應該是一個常量表達式。而類內只能初始化整型類型的靜態常量,所以不能在類內初始化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);

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/119304.html

相關文章

  • 系統地學習C++

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

    joyqi 評論0 收藏0
  • python怎么實現自動生成C++代碼

      小編寫這篇文章,主要目的還是給大家講一下關于python代碼的相關事宜,比如怎么才能夠實現自動生產C++代碼,這里面還是比較的復雜的,下面小編就給大家貼出具體的代碼給大家來看下。  遇到的問題  工作中遇到這么一個事,需要寫比較多的C++的底層數據庫類,但這些類大同小異,無非是增刪改查,如果人工來寫代碼,既費力又容易出錯;而借用python的代碼自動生成,可以輕松搞定;  (類比JAVA中的H...

    89542767 評論0 收藏0
  • ApacheCN 人工智能知識樹 v1.0

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

    劉厚水 評論0 收藏0
  • python協程1:yield 10分鐘入門

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

    MartinDai 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<