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第四版习题解答,人民邮电出版社。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值