筆記較為零散,都是自己不熟悉的知識點。
習題答案至於一個.cc 中,需要演示某一題直接修改 #define NUM***, 如運行7.23題為#define NUM723;
chapter 7
1、
定義在類內部的函數時隱式的inline函數
成員函數通過一個名為this的額外的參數來訪問調用它的那個對象,當我們調用一個成員函數時,用請求該函數的對象地址初始化this,比如調用total.isbn()時,
編譯器負責把total的地址傳遞給isbn的隱式形參this,可以等價認為編譯重寫為:
Sales_data::ibsn(&total),
其中,調用Sales_data的isbn成員時傳入;額total的地址。
this總是執行啊這個對象,所以this是一個常量指針。
this設置為指向常量的指針有助於提高函數的靈活性。
緊跟在參數列表之後的const表示this是一個指向常量的指針。這一函數稱為常量成員函數。
2、
一般來說,執行輸出任務的函數應該盡量減少對格式的控制,這樣可以確保由用戶代碼來決定是夠換行。
不同的構造函數之間必須在參數數量或參數類型上有所區別。
構造函數不能被聲明成const的。
默認的構造函數無需任何實參,編譯器創建的構造函數稱為合成的默認構造函數:
如果存在類內的初始值,用其來初始化成員。否則,默認初始化該成員。
如果在類外定義構造函數,因為其沒有返回值,必須指明是哪個類的成員。
使用this將對象作為一個整體訪問,而非直接訪問對象的某個成員。
使用*this將this對象作為實參傳遞給成員函數。
3、
當希望定義的類所有成員時public時,使用struct;反之,如希望成員時private,使用class。
友元聲明出現在類定義的內部,一般最好在類定義開始或結束前的位置集中聲明友元。
友元不是類成員,也不受它所在區域訪問級別的約束。
友元的聲明僅僅指定了訪問的權限,而非通常意義上的函數聲明。所以要在友元函數之外另起聲明。
4、
當我們提供一個類內初始值時,必須以符號=或花括號表示。
一個const成員函數如果以引用的形式返回*this,那麼它的返回類型將是常量引用。
類名用來直接指向類類型,也可以將類名跟於class/struct之後:
Sales_data item1;
class Sales_data item1; //等價的聲明
5、
友元關系不存在傳遞性,每個類負責控制自己的友元函數。
名字查找(name lookup)
現在在名字所在的塊中尋找其聲明語句,只考慮在名字的使用之前出現的聲明。
如果沒找到,繼續查找外層作用域。
最終沒找打,報錯。
類的定義分為兩步: 首先,編譯成員的聲明;直到類全部可見後才編譯函數體。
6、
類型名的定義通常出現在類的開始處,這樣能確保所有使用該類型的成員都出現在類名的定義之後。一般不建議使用其他成員的名字作為某個成員函數的參數。
成員是const、引用,或者屬於某種未提供默認構造函數的類類型,必須通過構造函數初始值為這些成員提供初值。
構造函數初始值的順序與成員聲明的順序保持一致。
7、
如果一個構造函數為所有參數都提供了默認實參,則它實際上也定義了默認的構造函數。
實際中,如果定義了其他構造函數,那麼最好也提供一個默認的構造函數。
8、
靜態成員函數不能與任何對象綁定在一起,它們不包含this指針。靜態成員函數也不能聲明成const的,並且也不能在static函數體內使用this指針。
非靜態數據成員不能作為默認實參,因為它的值本身屬於對象的一部分,如果這門做就無法真正提供一個對象以便從中獲取成員的值,引發錯誤。
#ifndef CHAPTER7_H #define CHAPTER7_H //Chapter7.h #include <iostream> #include <vector> using namespace std; class Sales_data; istream &read(istream &is, Sales_data &item); class Sales_data { friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs); friend istream &read(istream &is, Sales_data &item); friend ostream &print(ostream &os, const Sales_data &item); public: Sales_data(){}; Sales_data(const string &s):bookNo(s), units_sold(0), revenue(0.0){ } Sales_data(const string &s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p){ }; Sales_data(istream &is){ read(is, *this); } string isbn() const{ return bookNo; } Sales_data& combine(const Sales_data&); private: inline double avg_prive() const; string bookNo; unsigned units_sold; double revenue; }; Sales_data& Sales_data::combine(const Sales_data& rhs){ units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } double Sales_data::avg_prive() const{ return units_sold ? revenue / units_sold : 0; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs){ Sales_data sum = lhs; sum.combine(rhs); return sum; } istream &read(istream &is, Sales_data &item){ double price(0.0); is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } ostream &print(ostream &os, const Sales_data &item){ os << item.isbn() << " " << item.units_sold << " " <<item.revenue; return os; } class Person; istream &read(istream &is, Person &item); class Person{ friend istream &read(istream &is, Person &item); friend ostream &print(ostream &os, const Person &item); public: Person(){}; Person(const string &n, const string &addr): name(n), address(addr){ } Person(istream &is){ read(is, *this); } private: string name; string address; string getName() const { return name; } string getAddress()const { return address; } }; istream &read(istream &is, Person &p){ is >> p.name >> p.address; if(!is) p = Person(); return is; } ostream &print(ostream &os, const Person &p){ os << p.getName() << p.getAddress(); return os; } class Screen; class Window_mgr{ public: typedef vector<Screen>::size_type ScreenIndex; inline void clear(ScreenIndex); private: vector<Screen> screens; }; class Screen{ friend void Window_mgr::clear(ScreenIndex); public: typedef string::size_type pos; Screen(){} 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){} char get(){ return contents[cursor]; } char get(pos r, pos c) const { return contents[r * width + c]; } inline Screen &set(char c); inline Screen &set(pos r, pos c, char ch); inline Screen &move(pos r, pos c); const Screen &display(ostream &os) const{ os << contents; return *this; } pos size() const; private: pos cursor; pos height, width; string contents; }; Screen::pos Screen::size() const{ return height * width; } inline Screen &Screen::set(char c){ contents[cursor] = c; return *this; } inline Screen &Screen::set(pos r, pos col, char ch){ contents[r * width + col] = ch; return *this; } inline Screen &Screen::move(pos r, pos c){ pos row = r * width + c; cursor = row + c; return *this; } #endif
//main.cc #include <iostream> #include <vector> #include <cstring> #include "Chapter7.h" using namespace std; #define NUM758 /*7.1*/ #ifdef NUM71 struct Sales_data{ string bookNo; unsigned units_sold; double revenue; }; #endif /*7.2*/ #ifdef NUM72 struct Sales_data { string isbn() const{ return bookNo; } Sales_data& combine(const Sales_data&); string bookNo; unsigned units_sold; double revenue; }; Sales_data& Sales_data::combine(const Sales_data& rhs){ units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } #endif /*7.4*/ #ifdef NUM74 class Person{ string name; string address; }; #endif /*7.5*/ #ifdef NUM75 class Person{ string name; string address; string getName() const { return name; } string getAddress()const { return address; } }; #endif /*7.31*/ #ifdef NUM731 class Y; class X{ Y *p = nullptr; }; class Y{ X b; }; #endif /*7.35*/ #ifdef NUM735 typedef string Type; Type initVal(); //use 'string' class Exercise{ public: typedef double Type; Type setVal(Type); //use 'double' Type initVal(){ //use 'double' return val; } private: int val; }; Exercise::Type Exercise::setVal(Type parm){ //first uses 'string', second uses 'double' val = parm + initVal(); //use 'double' return val; } #endif /*7.58*/ #ifdef NUM758 class Example{ public: static double rate; static const int vecSize = 20; static vector<double> vec; }; #endif int main(){ /*7.1*/ #ifdef NUM71 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{ cerr<< "No data ?"<<endl; return -1; } #endif /*7.2*/ #ifdef NUM72 cout<<"見main之前類聲明. "<<endl; #endif /*7.3*/ #ifdef NUM73 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{ cerr<< "No data ?"<<endl; return -1; } #endif /*7.4*/ #ifdef NUM74 cout<<"見main之前類聲明. "<<endl; #endif /*7.5*/ #ifdef NUM75 cout << "需要加const, 表示這是一個指向const類型的指針. "<<endl; #endif /*7.6*/ #ifdef NUM76 cout<<"見main之前的函數定義. "<<endl; #endif /*7.7*/ #ifdef NUM77 Sales_data total; if(read(cin, total)){ Sales_data trans; while(read(cin, trans)){ if(total.isbn() == trans.isbn()) total.combine(trans); else{ print(cout, trans) << endl; total = trans; } } print(cout, total) << endl; }else{ cerr<< "No data ?"<<endl; return -1; } #endif /*7.8*/ #ifdef NUM78 cout << "read用普通引用,因為它將改變類的成員值,print用常量引用,因為它不會改變對象的成員值. "<<endl; #endif /*7.9*/ #ifdef NUM79 cout << "見Chapter7.h"<<endl; #endif /*7.10*/ #ifdef NUM710 cout << "if條件可以同時讀取類的兩個對象." <<endl; #endif /*7.11*/ #ifdef NUM711 Sales_data item1; //沒有() print(cout, item1) << endl; Sales_data item2("123X"); print(cout, item2) << endl; Sales_data item3("123X", 3, 12.00); print(cout, item3) << endl; Sales_data item4(cin); print(cout, item4) << endl; #endif /*7.13*/ #ifdef NUM713 Sales_data total(cin); if(!total.isbn().empty()){ istream &is = cin; while(is){ Sales_data trans(is); if(total.isbn() == trans.isbn()) total.combine(trans); else{ print(cout, total) << endl; total = trans; } } }else{ cerr<< "No data ?"<<endl; return -1; } #endif /*7.14*/ #ifdef NUM714 cout<<"見Chapter7.h構造函數聲明. "<<endl; #endif /*7.15*/ #ifdef NUM715 cout << "見Chapter7.h"<<endl; #endif /*7.16*/ #ifdef NUM716 cout << "沒有限制,對於能夠被程序的所有部分訪問的成員置於public之public之後,對於那些只希望被成員函數訪問的成員置於private之後. " <<endl; #endif /*7.17*/ #ifdef NUM717 cout <<"基本一樣,但是訪問權限不同; struct定義在第一個訪問說明符之前的成員是public, class關鍵字在第一個訪問說明符之前的是private. "<<endl; #endif /*7.18*/ #ifdef NUM718 cout <<"封裝實現了接口和具體實現的分離; 好處就是用戶層面的代碼不能破壞封裝對象的狀態,實現了封裝類的細節隱藏. "<<endl; #endif /*7.19*/ #ifdef NUM719 cout <<"name/addres被聲明為private,getName/getAddress被聲明為public,接口需要被定義為公開的,而數據不應該暴露在類之外. "<<endl; #endif /*7.20*/ #ifdef NUM720 cout <<"在允許其他類訪問一個類的非公有成員時. 好處是方便的訪問非共有數據,而不用顯式的添加前綴來訪問。弊處是降低了封裝型,造成了代碼的冗余. "<<endl; #endif /*7.21*/ #ifdef NUM721 cout <<"直接在.h中修改,包括添加友元函數聲明,添加public/private作用符.運行7.13和7.7中的程序實現. "<<endl; #endif /*7.23*/ #ifdef NUM723 cout << "見Chapter7.h"<<endl; #endif /*7.24*/ #ifdef NUM724 cout << "見Chapter7.h"<<endl; #endif /*7.25*/ #ifdef NUM725 cout <<"能夠依賴,聚合版本對於string和vector類型的拷貝賦值有效."<<endl; #endif /*7.26*/ #ifdef NUM726 cout << "見Chapter7.h, Sales_data類"<<endl; #endif /*7.27*/ #ifdef NUM727 Screen myScreen(5, 5, 'X'); myScreen.move(4, 0).set('#').display(cout); cout << "\n"; myScreen.display(cout); cout << "\n"; #endif /*7.28*/ #ifdef NUM728 cout << "有&, 打印:\n" "XXXXXXXXXXXXXXXXXXXX#XXXX \n" "XXXXXXXXXXXXXXXXXXXX#XXXX" "沒有&,打印:\n" "XXXXXXXXXXXXXXXXXXXX#XXXX \n" "XXXXXXXXXXXXXXXXXXXXXXXXX" <<endl; #endif /*7.30*/ #ifdef NUM730 cout<<"優點:顯示的,容易閱讀;缺點,有時候會多余,比如可以直接返回成員時用this->"<<endl; #endif /*7.31*/ #ifdef NUM731 cout<<"見main之前類聲明. "<<endl; #endif /*7.32*/ #ifdef NUM732 cout << "見Chapter7.h"<<endl; #endif /*7.33*/ #ifdef NUM733 cout << "報錯,找不到pos類型,修改在pos前加上Screen. " #endif /*7.34*/ #ifdef NUM734 cout << "報錯pos未定義. "<<endl; #endif /*7.35*/ #ifdef NUM735 cout << "類外的initval是string類型,類內的Type都是double類型,setVal定義中第一個Type是string類型,第二個是double類型。 修改: Tpye返回類型前加上Exercise, 同時定義initVal成員函數. "<<endl; #endif /*7.36*/ #ifdef NUM736 cout <<"因為構造函數中base先初始化,rem用base來初始化,但是base的聲明在後,所以此時rem初始化參數是未聲明的. "<<endl; struct X{ X(int i, int j): base(i), rem(base % j) {} int base, rem; }; #endif /*7.37*/ #ifdef NUM737 cout <<"第一句使用Sales_data(std::istream &is); 第二句使用Sales_data(string s = ""): bookNo(""), cnt = 0, revenue = 0.0; 第三句使用Sales_data(std::string s = ""); bookNo = "9-999-99999-9", cnt = 0, revenue = 0.0 "<<endl; #endif /*7.38*/ #ifdef NUM738 cout << "Sales_data(istream &is = cin){ read(is, *this); } "<<endl; #endif /*7.39*/ #ifdef NUM739 cout <<"是非法的,Sales_data()構造函數重載,聲明沖突."<<endl; #endif /*7.40*/ #ifdef NUM740 class Book { public: Book() = default; Book(unsigned no, string name, string author, string pubdate):no_(no), name_(name), author_(author), pubdate_(pubdate) { } Book(istream &in) { in >> no_ >> name_ >> author_ >> pubdate_; } private: unsigned no_; string name_; string author_; string pubdate_; }; #endif /*7.41*/ #ifdef NUM741 cout << "1.Default way: "<<endl; Sales_data s1; cout << "2. usng string as parameter: "<<endl; Sales_data s2("c++ & linux"); cout << "3. complete parameters: " <<endl; Sales_data s3("c++ & linux", 3, 20.8); cout << "4. using istream as parameter: "<<endl; Sales_data s4(cin); #endif /*7.42*/ #ifdef NUM742 class Book { public: Book(unsigned no, std::string name, std::string author, std::string pubdate):no_(no), name_(name), author_(author), pubdate_(pubdate) { } Book() : Book(0, "", "", "") { } Book(std::istream &in) : Book() { in >> no_ >> name_ >> author_ >> pubdate_; } private: unsigned no_; std::string name_; std::string author_; std::string pubdate_; }; #endif /*7.43*/ #ifdef NUM743 class NoDefault{ public: NoDefault(int){} }; class C{ public: C(): member(0){} //定義C的構造函數 private: NoDefault member; }; #endif /*7.44*/ #ifdef NUM744 cout <<"不合法,應為vector有10個元素,每個都必須被默認初始化。但臨時變量顯然沒有默認初始化. "<<endl; #endif /*7.45*/ #ifdef NUM745 cout << "可以,因為C定義了默認構造函數. "<<endl; #endif /*7.46*/ #ifdef NUM746 cout <<"(a)不正確,沒有構造函數編譯器生成合成默認構造函數." "(b)不正確,默認構造函數是在沒有初始值提供的情況下,或者類本身含有類類型成員且使用合成的默認構造函數時." "(c)不正確,都需要提供構造函數." "(d)不正確,只有當類沒有顯式的定義任何構造函數時,編譯器才會生成默認構造函數." #endif /*7.47*/ #ifdef NUM747 cout <<"可以,防止string被編譯器隱式轉換成Sales_data對象.優點是防止構造函數發生隱式轉換。缺點是只能以直接初始化的形式使用." #endif /*7.48*/ #ifdef NUM748 cout<<"1.創建了一個臨時的string對象,然後調用string的拷貝構造函數,將null_isbn初始化為該臨時對象的副本。2.使用string對象null_isbn為實參,調用Sales_data構造函數創建對象null。3.生成string臨時對象,調用臨時對象作為實參,調用Sales_data構造函數創建對象null. 如果Sales_data的構造函數時顯式的,那麼第三句將是未定義的。" #endif /*7.49*/ #ifdef NUM749 cout <<"(a)通過 (b)編譯出錯,string類型不能轉換成Sales_data類型. (c)combine是操作不能定義為const Sales_data類型."<<endl; #endif /*7.50*/ #ifdef NUM750 cout <<"輸入可以聲明為顯示的,explicit Person(std::istream& is) { read(is, *this); } "<<endl; #endif /*7.51*/ #ifdef NUM751 cout <<"為符合C風格的編寫風格,string用來代替const char*, 所以我們可以使用'linux'轉換成string;但是vector不是,當vector&作為形參是不用整形來隱式替換." #endif /*7.52*/ #ifdef NUM752 cout <<"錯誤,聚合類內沒有初始值。應該將64頁中units_sold和revenue的初始值去除." #endif /*7.53*/ #ifdef NUM753 class Debug{ public: constexpr Debug(bool b = true) : rt(b), io(b), other(b) {} constexpr Debug(bool r, bool i, bool o) : rt(r), io(i), other(0) {} constexpr bool any() { return rt || io || other; } void set_rt(bool b) { rt = b; } void set_io(bool b) { io = b; } void set_other(bool b) { other = b; } private: bool rt; // runtime error bool io; // I/O error bool other; // the others }; #endif /*7.54*/ #ifdef NUM754 cout <<"不可以,因為constexpr擁有的唯一可執行的語句是可執行語句."<<endl; #endif /*7.55*/ #ifdef NUM755 cout << "不是字面值常量類. " #endif /*7.56*/ #ifdef NUM756 cout<<"靜態成員存在於任何對象之外,對象中不包含任何與靜態成員相關的數據.不是類的單個對象. " "優點,封裝,statis成員可以是私有成員,而全局對象不行.每個對象不需要存儲常量數據,如果數據變化了,每個對象就會使用新值. " "不同點,靜態成員可以是不完全類型,靜態成員可以作為默認實參." #endif /*7.57*/ #ifdef NUM757 cout <<"見Chapter7.h"<<endl; #endif /*7.58*/ #ifdef NUM758 Example::rate; Example::vec; cout <<"見編譯結果,rate應該是常量表達式,去掉6.5的賦值,vec的靜態聲明不能指定類中初始化的參數,改為 static vector<double> vec; vector<double>;"<<endl; #endif return 0; }參考資料:c++ primer中文版第五版,電子工業出版社。
c++ primer第四版習題解答,人民郵電出版社。