歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

c++ primer(第五版)學習筆記及習題答案代碼版(第七章)類

筆記較為零散,都是自己不熟悉的知識點。

習題答案至於一個.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第四版習題解答,人民郵電出版社。

Copyright © Linux教程網 All Rights Reserved