摘要:構(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ù)。
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ù)total.isbn()
當(dāng)isbn返回bookNo時(shí),實(shí)際上它隱式的返回total.bookNo。成員函數(shù)通過一個(gè)名為this
的額外的隱式參數(shù)來訪問調(diào)用它的那個(gè)對(duì)象。this
偽代碼:Sales_data::isbn(&total)
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
保存的地址)。和類相關(guān)的非成員函數(shù),定義和聲明都應(yīng)該在類的外部。
一般來說,如果非成員函數(shù)是類接口的組成部分,則這些函數(shù)的聲明應(yīng)該與類在同一個(gè)頭文件中。
public
部分= default
要求編譯器合成默認(rèn)的構(gòu)造函數(shù)。(C++11)Sales_item(): units_sold(0), revenue(0.0) { }
、public
:定義在public
后面的成員在整個(gè)程序內(nèi)可以被訪問;public
成員定義類的接口private
:定義在private
后面的成員可以被類的成員函數(shù)訪問,但不能被使用該類的代碼訪問;private
隱藏了類的實(shí)現(xiàn)細(xì)節(jié)。class
和struct
:都可以被用于定義一個(gè)類,唯一的區(qū)別在于默認(rèn)訪問權(quán)限。 class
:在第一個(gè)訪問說明符之前的成員是private
的。struct
:在第一個(gè)訪問說明符之前的成員是public
的。類可以允許其他類或者函數(shù)訪問它的非公有成員,方法是令其他類或者函數(shù)成為它的友元。如果類想把一個(gè)函數(shù)作為他的友元,只需要增加一條以friend
關(guān)鍵字開始的函數(shù)聲明語句即可。
friend
開始, friend Sales_data add(const Sales_data&, const Sales_data&);
表示非成員函數(shù)add
可以訪問類的非公有成員。inline
inline
。mutable
關(guān)鍵字:mutable data access_ctr;
const
,即使他是const
對(duì)象的成員const
或者是引用的話,必須將其初始化。只能初始化,不能賦值(注意初始化和賦值的區(qū)別) Sales_data(): Sales_data(" ", 0, 0) { }
只有當(dāng)一個(gè)類沒有定義 任何構(gòu)造函數(shù)的時(shí)候,編譯器才會(huì)生成一個(gè)默認(rè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)換
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ù)用于拷貝形式的初始化過程
explicit
的構(gòu)造函數(shù)用于隱式轉(zhuǎn)換過程,但是我們可以使用這樣的構(gòu)造函數(shù) 顯式地強(qiáng)制進(jìn)行轉(zhuǎn)換。聚合類使得用戶可以直接訪問其成員,并且具有特殊的初始化語法形式。
滿足以下所有條件:
public
的virtual
函數(shù)可以使用一個(gè)花括號(hào)括起來的成員初始化列表,并用它初始化聚合類的數(shù)據(jù)成員。初始值的順序必須與聲明的順序一致。
constexpr
函數(shù)的參數(shù)和返回值必須是字面值類型constexpr
構(gòu)造函數(shù)。constexpr
構(gòu)造函數(shù)。static
數(shù)據(jù)成員存在于類類型的每個(gè)對(duì)象中。static
數(shù)據(jù)成員獨(dú)立于該類的任意對(duì)象而存在。static
數(shù)據(jù)成員是與類關(guān)聯(lián)的對(duì)象,并不與該類的對(duì)象相關(guān)聯(lián)。static
::
直接訪問靜態(tài)成員 r = Account::rate()
r = ac1.rate()
使用指針訪問:r = ac2->rate()
static
關(guān)鍵字,該關(guān)鍵字只出現(xiàn)在類內(nèi)部的聲明語句。static
。double Account::interestRate = initRate();
constexpr
。使用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;}
曾在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;}
修改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;}
編寫一個(gè)名為Person
的類,使其表示人員的姓名和地址。使用string
對(duì)象存放這些元素,接下來的練習(xí)將不斷充實(shí)這個(gè)類的其他特征。
解:
#include class Person { std::string name; std::string address;};
在你的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ù)操作。
對(duì)于函數(shù)add
、read
和print
,定義你自己的版本。
解:
#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;}
使用這些新函數(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;}
為什么read
函數(shù)將其Sales_data
參數(shù)定義成普通的引用,而print
函數(shù)將其參數(shù)定義成常量引用?
解:
因?yàn)?code>read函數(shù)會(huì)改變對(duì)象的內(nèi)容,而print
函數(shù)不會(huì)。
對(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;}
在下面這條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ì)象。
在你的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;}
把只接受一個(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;}
使用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;}
編寫一個(gè)構(gòu)造函數(shù),令其用我們提供的類內(nèi)初始值顯式地初始化成員。
Sales_data() : units_sold(0) , revenue(0) { }
為你的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;}
在類的定義中對(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
。
使用class
和struct
時(shí)有區(qū)別嗎?如果有,是什么?
解:
class
和struct
的唯一區(qū)別是默認(rèn)的訪問級(jí)別不同。
封裝是何含義?它有什么用處?
將類內(nèi)部分成員設(shè)置為外部不可見,而提供部分接口給外面,這樣的行為叫做封裝。
用處:
在你的Person
類中,你將把哪些成員聲明成public
的? 哪些聲明成private
的? 解釋你這樣做的原因。
構(gòu)造函數(shù)、getName()
、getAddress()
函數(shù)將設(shè)為public
。 name
和 address
將設(shè)為private
。 函數(shù)是暴露給外部的接口,因此要設(shè)為public
; 而數(shù)據(jù)則應(yīng)該隱藏讓外部不可見。
修改你的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;}
修改你的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;}
編寫你自己的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;};
給你的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;};
給你自己的Screen
類添加move
、set
和display
函數(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;}
如果move
、set
和display
函數(shù)的返回類型不是Screen&
而是Screen
,則在上一個(gè)練習(xí)中將會(huì)發(fā)生什么?
解:
如果返回類型是Screen
,那么move
返回的是*this
的一個(gè)副本,因此set
函數(shù)只能改變臨時(shí)副本而不能改變myScreen
的值。
修改你的Screen
類,令move
、set
和display
函數(shù)返回Screen
并檢查程序的運(yùn)行結(jié)果,在上一個(gè)練習(xí)中你的推測正確嗎?
解:
推測正確。
#with "&"XXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXX#XXXX ^# without "&"XXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXX ^
定義一對(duì)類X
和Y
,其中X
包含一個(gè)指向Y
的指針,而Y
包含一個(gè)類型為X
的對(duì)象。
class Y;class X { Y* y = nullptr;};class Y { X x;};
定義你自己的Screen
和Window_mgr
,其中clear
是Window_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;}
如果我們把第256頁Screen
類的pos
的typedef
放在類的最后一行會(huì)發(fā)生什么情況?
解:
在 dummy_fcn(pos height) 函數(shù)中會(huì)出現(xiàn) 未定義的標(biāo)識(shí)符pos。
類型名的定義通常出現(xiàn)在類的開始處,這樣就能確保所有使用該類型的成員都出現(xiàn)在類名的定義之后。
有些情況下我們希望提供cin
作為接受istream&
參數(shù)的構(gòu)造函數(shù)的默認(rèn)實(shí)參,請(qǐng)聲明這樣的構(gòu)造函數(shù)。
解:
Sales_data(std::istream &is = std::cin) { read(is, *this); }
使用委托構(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)用順序是:
假定有一個(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;};
說明接受一個(gè)string
參數(shù)的Sales_data
構(gòu)造函數(shù)是否應(yīng)該是explicit
的,并解釋這樣做的優(yōu)缺點(diǎn)。
解:
是否需要從string
到Sales_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ù)
假定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)。
對(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ì)象
確定在你的Person
類中是否有一些構(gòu)造函數(shù)應(yīng)該是explicit
的。
explicit Person(std::istream& is) { read(is, *this); }
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è)元素的vecto
r的臨時(shí)量,然后返回34。但是這樣沒有任何意義。而string
則不同,string
的單參數(shù)構(gòu)造函數(shù)的參數(shù)是const char *
,因此凡是在需要用到string
的地方都可以用const char *
來代替(字面值就是const char *
)。如:
void print(std::string);print("hello world");
使用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;};
定義你自己的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};
Debug
中以 set_
開頭的成員應(yīng)該被聲明成constexpr
嗎?如果不,為什么?
解:
不能。constexpr
函數(shù)必須包含一個(gè)返回語句
7.5.5節(jié)的Data
類是字面值常量類嗎?請(qǐng)解釋原因。
解:
不是。因?yàn)?code>std::string不是字面值類型。
編寫你自己的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();
下面的靜態(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
摘要:本書主要圍繞一系列逐漸復(fù)雜的程序問題,以及用以解決這些問題的語言特性展開講解。你不只學(xué)到的函數(shù)和結(jié)構(gòu),也會(huì)學(xué)習(xí)到它們的設(shè)計(jì)目的和基本原理。因此我們把精力集中在最有價(jià)值的地方。本書不僅是對(duì)模板的權(quán)威解釋,而且本書還深入地介紹了其他一般的思想。 C++ 入門教程(41課時(shí)) - 阿里云大學(xué) 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...
摘要:貢獻(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)者:飛龍版...
摘要:協(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...
閱讀 1653·2021-09-26 09:55
閱讀 1386·2021-09-23 11:22
閱讀 2749·2021-09-06 15:02
閱讀 2658·2021-09-01 11:43
閱讀 3977·2021-08-27 13:10
閱讀 3692·2021-08-12 13:24
閱讀 2080·2019-08-30 12:56
閱讀 3009·2019-08-30 11:22