C++ primer 5th 第7章 类
=====================================================================
第7章 类 228页 设计Sales_data类
=====================================================================
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
#include <string>
#include <iostream>
using namespace std;
class Sales_data
{
public:
//Sales_data的非成员接口函数
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);
std::string isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
#ifdef DECLARATION
//伪代码,说明隐式的this是如何使用的
//下面代码是非法的,因为不能显式的定义自己的this指针
//谨记此处的this是一个指向常量的指针,因为isbn是一个常量成员
std::string Sales_data::isbn(const Sales_data* const this)
{
return this->isbn;
}
#endif//DECLARATION
Sales_data& combine(const Sales_data&);
double ave_price()const;
private:
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
double Sales_data::ave_price()const
{
if (units_sold)
return revenue / units_sold;
else
{
return 0;
}
}
//函combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os,const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
#endif
―――――――――――――――――――――――――――――――――――――――
#include "Sales_data.h"
#include<iostream>
using namespace std;
int main()
{
Sales_data total;//保存当前求和结果的变量
if (cin>>total/*read(cin,total)*/)//读入第一笔交易
{
Sales_data trans;//保存下一条交易的数据的变量
while (read(cin, trans))//读入剩余的交易
{
if (total.isbn() == trans.isbn())//检查isbn
total.combine(trans);//更新变量total当前的值
else
{
print(cout, total) << endl;//输出结果
total = trans;//处理下一本书
}
}
print(cout, total) << endl;//输出最后一条交易
}
else
{
cerr<<"No data"<<endl;//通知用户
}
/*
一个isbn成员函数,用于返回对象的INSB编号
一个combine的成员函数,用于将一个Sales_data对象加到另一个对象上
一个名为add的函数,执行两个Sales_data对象的加法
一个read函数,将数据从istream读入到Sales_data
一个printf函数,将Sales_data对象输出到ostream
*/
system("pause");
return 0;
}
=====================================================================
第7章 类 233页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
/*
练习7.2:曾在2.6.2节的练习(第76页)中编写了一个Sales_data类,请向这个类添加combine和isbn
成员。*/
struct Sales_data
{
Sales_data& combine(const Sales_data& rhs);
string& isbn();
double ave_price();
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;
}
string& Sales_data::isbn()
{
return bookNo;
}
double Sales_data::ave_price()
{
if (units_sold)
return revenue / units_sold;
else
return 0;
}
/*
练习7.4:编写一个名为Person的类,使其表示人员的姓名和住址。使用string对象存放这些元素,接
下来的练习将不断充实这个类的其他特征。*/
class Persor
{
public://把strName_与strAddress_定义成公有public方便在类外访问
string getName()const
{
return strName_;
}
string getAccess()const//两个函数应该被定义成常量函数,不论读取还是返回都不会改变值
{
return strAddress_;
}
string strName_;//姓名
string strAddress_;//地址
};
int main()
{
//输入非数字
cout<<"输入数字并校验"<<endl;
int num=0,num1=0;
while (!(cin>> num>>num1))//输入数字表示为真.结果为假.结束
{
cin.clear();//执行到这里表达输入的为字符等其他,为假,结果为真,进入循环,清除流缓存
while (cin.get() != '\n')//读取行尾所有输入,并删除.
continue;
cout <<"输入并不是数字请重新输入" << endl;
}
cout << "输入正确" << endl;
//结束
//练习7.4:
cout << "输入Persor类成员并输出" << endl;
cin.clear();
Persor persor;
cin >> persor.strName_ >> persor.strAddress_;
cout << persor.getName() << "\t" << persor.getAccess() << endl;
/*练习7.3:修改7.1.1节(第229页)的交易处理程序,令其使用这些成员。*/
cout<<"输入Sales_data数据并统计结果"<<endl;
Sales_data total;
double price = 0;
if(cin>>total.bookNo>>total.units_sold>>price)
{
total.revenue = total.units_sold*price;
Sales_data trans;
while (cin>>trans.bookNo>>trans.units_sold>>trans.revenue)
{
trans.revenue = total.units_sold*price;
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
cout << total.bookNo << "\t" << total.units_sold << "\t"
<< total.revenue << "\t" << total.ave_price() << endl;
total = trans;
}
}
cout << total.bookNo << "\t" << total.units_sold << "\t"
<< total.revenue << "\t" << total.ave_price() << endl;
}
else
{
cout << "No data" << endl;
}
system("pause");
return 0;
}
=====================================================================
第7章 类 235页 练习题
=====================================================================
//QQ108201645编写
#include<iostream>
#include<string>
using namespace std;
class Sales_data
{
public://公有成员
friend Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum(lhs);
sum.combine(rhs);
return sum;
}
friend istream& read(istream& is, Sales_data& item)
{
double price = 0;
cout << "输入售出的书的编号:";
is >> item.bookNo;
cout << "输入售出的书的数量:";
is >> item.units_sold;
cout << "输入售出的书的价格:";
is >> price;
item.revenue = price * item.units_sold;
cout<<endl;
return is;
}
friend ostream& print(ostream& os, const Sales_data& item)
{
os<<"书的编号:"<< item.bookNo<<" 书的数量:"<< item.units_sold
<<" 书的销售额:"<< item.revenue<<" 书的平均价格:"
<< item.revenue/ item.units_sold<<endl;
return os;
}
Sales_data& combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
string isbn()const
{
return bookNo;
}
double ave_price()const
{
if (units_sold)
return revenue / units_sold;
else
return 0;
}
private://私有成员
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
/*练习7.9:对于7.1.2节(第233页)练习中的代码,添加读取和打印Person对象的操作*/
class Persor
{
public://把strName_与strAddress_定义成公有public方便在类外访问
friend istream& read(istream& is, Persor& item)
{
is >> item.strName_ >> item.strAddress_;
return is;
}
friend ostream& print(ostream& os, Persor& item)
{
os << item.getName() << " " << item.getAccess();
return os;
}
string getName()const
{
return strName_;
}
string getAccess()const//两个函数应该被定义成常量函数,不论读取还是返回都不会改变值
{
return strAddress_;
}
string strName_;//姓名
string strAddress_;//地址
};
int main()
{
/*练习7.7:使用这些新函数重写7.1.2节(第233页)练习中的交易处理程序*/
Sales_data total;
if (read(cin, total))
{
Sales_data trans;
while (read(cin,trans)&&trans.isbn()!="end")//第二次输入后如果bookNo是end就结束
{
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
print(cout,total)<<endl;
total = trans;
}
cout << endl;
}
print(cout, total)<<endl;
}
else
{
cout<<"No data"<<endl;
}
/*练习7.8:为什么read函数将其Sales_data参数定义成普通的引用,而print函数将
其参数定义成常量引用?
因为read函数要对形参进行写入操作,有修改对象的权限,而print只需要读取操作,不对其
做任何修改。*/
/*练习7.9*/
Persor persor;
read(cin, persor);
print(cout, persor);
/*练习7.10:在下面这条if语句中,条件部分的作用是什么?
if (read(read(cin, data1), data2))
从cin流连续读取data1、data2两个对象*/
Persor data1, data2;
read(read(cin, data1), data2);
print(print(cout, data1), data2);
system("pause");
return 0;
}
=====================================================================
第7章 类 236页 合成的默认构造函数
=====================================================================
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
#include <string>
#include <iostream>
using namespace std;
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
//Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
Sales_data(const std::string &s)
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)
Sales_data(const std::string &s,unsigned n,double p)
:bookNo(s),units_sold(n),revenue(p*n){}
Sales_data(std::istream&);//构造函数( 可以直接在函数声明的地方为istream&类型的参数设置默认实参cin)
//练习7.14:编写一个构造函数,令其用我们提供的类内初始值显式地初始化成员。
Sales_data():units_sold(0),revenue(0){}//使用这句后,Sales_data() = default;
//Sales_data的非成员接口函数
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
double ave_price()const;
private://私有成员
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data::Sales_data(std::istream &is)
{
read (is, *this);//调用std::istream& read(std::istream&, Sales_data&);
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
double Sales_data::ave_price()const
{
if (units_sold)
return revenue / units_sold;
else
{
return 0;
}
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
#endif
―――――――――――――――――――――――――――――――――――――――
#include "Sales_data.h"
#include<iostream>
using namespace std;
class Persor
{
public://把strName_与strAddress_定义成公有public方便在类外访问
//练习7.15:为你的 Person 类添加正确的构造函数。
Persor() = default;
Persor(const string& strName)
:strName_(strName){}
Persor(const string& strName,const string &strAddress)
:strName_(strName),strAddress_(strAddress){}
Persor(std::istream& is)
{
is >> *this;//需要operator>>重载
}
friend istream& read(istream& is, Persor& item)
{
is >> item.strName_ >> item.strAddress_;
return is;
}
friend istream& operator>>(istream& is, Persor& item)
{
is >> item.strName_ >> item.strAddress_;
return is;
}
friend ostream& print(ostream& os, Persor& item)
{
os << item.getName() << " " << item.getAccess();
return os;
}
string getName()const
{
return strName_;
}
string getAccess()const//两个函数应该被定义成常量函数,不论读取还是返回都不会改变值
{
return strAddress_;
}
string strName_;//姓名
string strAddress_;//地址
};
int main()
{
//练习7.11:在你的Sales_data类中添加构造函数,然后编写一段程序令其用到每个构造函数。
Sales_data object1, object2("123"), object3("123", 5, 15);
//练习7.12:把只接受一个istream 作为参数的构造定义函数移到类的内部。
//练习7.13与7.14使用istream构造函数重写第229页的程序。查看不起Sales_data类
Sales_data object4(cin);//调用Sales_data(std::istream&);
cout<<object1<<"\t"<<object2<<"\t"<<object3<<"\t"<<object4<<"\t"<<endl;
Persor persor(cin);
print(cout, persor);
cout << endl;
system("pause");
return 0;
}
=====================================================================
第7章 类 237页 拷贝、赋值和析构
=====================================================================
//QQ108201645编写
#include<iostream>
#include <string>
using namespace std;
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
//Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
Sales_data(const std::string &s)
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)
Sales_data(const std::string &s, unsigned n, double p)
:bookNo(s), units_sold(n), revenue(p*n) {}
Sales_data(std::istream&);//构造函数( 可以直接在函数声明的地方为istream&类型的参数设置默认实参cin)
Sales_data() :units_sold(0), revenue(0) {}//使用这句后,Sales_data() = default;
//Sales_data的非成员接口函数
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
double ave_price()const;
//private://私有成员
public://必须声明成公有成员才能在类体外访问
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data::Sales_data(std::istream &is)
{
read(is, *this);//调用std::istream& read(std::istream&, Sales_data&);
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
double Sales_data::ave_price()const
{
if (units_sold)
return revenue / units_sold;
else
{
return 0;
}
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
#endif
int main()
{
Sales_data total, trans;
if (read(cin, total))
{
while (read(cin,trans))
{
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
print(cout, total);
//total = trans;
//等同于下面,Sales_
total.bookNo = trans.bookNo;
total.units_sold = trans.units_sold;
total.revenue = trans.revenue;
}
}
}
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
#include <string>
#include <iostream>
using namespace std;
/*定义在public:说明符之后的成员在整个程序内可被访问public成员定义类的
接口*/
/*定义在private:说明符之后成员只被类的函数访问,但不能被使用
该类的代码访问,private封装了(即隐藏了)类的实现细节*/
//使用class而非struct开始类的定义,只是形式不同,唯一区别就是默认访问权限不同
//没有定义public: protected: private: 的情况下.在struct里都是public
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
//Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
Sales_data(const std::string &s)
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)
Sales_data(const std::string &s,unsigned n,double p)
:bookNo(s),units_sold(n),revenue(p*n){}
Sales_data(std::istream&);//构造函数( 可以直接在函数声明的地方为istream&类型的参数设置默认实参cin)
Sales_data():units_sold(0),revenue(0){}//使用这句后,Sales_data() = default;
//Sales_data的非成员接口函数,但可以访问类的非公有成员或成员函数
//友元函数(friend)不是类的成员或成员函数
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
private://私有成员
double ave_price()const;
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
Sales_data::Sales_data(std::istream &is)
{
read(is, *this);//调用std::istream& read(std::istream&, Sales_data&);
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
double Sales_data::ave_price()const
{
return units_sold ? revenue / units_sold : 0;
}//等同于下面
#ifdef DECLARATION
double Sales_data::ave_price()const
{
if (units_sold)
return revenue / units_sold;
else
{
return 0;
}
}
#endif//DECLARATION
#endif
―――――――――――――――――――――――――――――――――――――――
#include "Sales_data.h"
#include<iostream>
using namespace std;
int main()
{
Sales_data total, trans;
if (read(cin, total))
{
while (read(cin,trans))
{
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
print(cout, total);
total = trans;
}
}
}
system("pause");
return 0;
}
#ifdef DECLARATION
练习7.16:在类的定义中对于访问说明符出现的位置和次数有限定吗?如果有,是什么?什么样的成员应该定义在public说明符之后?什么样的成员应该定义在private说明符之后?
没有限制。对于那些属于类的接口的部分,应当出现在public后面,而那些具体实现或者数据成员则应该在private后面
练习7.17:使用class 和 struct 时有区别吗?如果有,是什么?
有,在没有访问说明符时,默认的访问权限不同。
练习7.18:封装是何含义?它有什么用处?
封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口。
练习7.19:在你的Person 类中,你将把哪些成员声明成public的?哪些声明成private的?解释你这样做的原因。
将数据成员声明成private的,函数成员声明成public的。因为Person类不需要直接提供数据操作给外部,只需要相应的功能接口。
#endif
=====================================================================
第7章 类 241页 友元
=====================================================================
//QQ108201645编写
#include<iostream>
#include <string>
using namespace std;
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
/*定义在public:说明符之后的成员在整个程序内可被访问public成员定义类的
接口*/
/*定义在private:说明符之后成员只被类的函数访问,但不能被使用
该类的代码访问,private封装了(即隐藏了)类的实现细节*/
//使用class而非struct开始类的定义,只是形式不同,唯一区别就是默认访问权限不同
//没有定义public: protected: private: 的情况下.在struct里都是public
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
//Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
Sales_data(const std::string &s)
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)
Sales_data(const std::string &s, unsigned n, double p)
:bookNo(s), units_sold(n), revenue(p*n) {}
Sales_data(std::istream&);//构造函数( 可以直接在函数声明的地方为istream&类型的参数设置默认实参cin)
Sales_data() :units_sold(0), revenue(0) {}//使用这句后,Sales_data() = default;
//Sales_data的非成员接口函数,但可以访问类的非公有成员或成员函数
//友元函数(friend)不是类的成员或成员函数
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
private://私有成员
double ave_price()const;
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
Sales_data::Sales_data(std::istream &is)
{
read(is, *this);//调用std::istream& read(std::istream&, Sales_data&);
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
double Sales_data::ave_price()const
{
return units_sold ? revenue / units_sold : 0;
}//等同于下面
#ifdef DECLARATION
double Sales_data::ave_price()const
{
if (units_sold)
return revenue / units_sold;
else
{
return 0;
}
}
#endif//DECLARATION
#endif
int main()
{
Sales_data total, trans;
if (read(cin, total))
{
while (read(cin,trans))
{
if (total.isbn() == trans.isbn())
total.combine(trans);
else
{
print(cout, total);
total = trans;
}
}
}
#ifdef DECLARATION
练习7.20:友元在什么时候有用?请分别列举出使用友元的利弊。
在需要直接访问类的private部分的内容时有用,友元有时候能够避免开销,但也破坏了封装。
练习7.21:修改你的Sales_data类使其隐藏实现的细节。你之前编写的关于
Sales_data操作的程序应该继续使用,借助类的新定义重新编译该程序,确保其正常工作。
参考原先写的Sales_data类里的private:成员
练习7.22:修改你的Person类使其隐藏实现的细节。
参考原先写的Person类里的private : 成员
#endif
system("pause");
return 0;
}
=====================================================================
第7章 类 243页 定义一个类型成员Screen
=====================================================================
//QQ108201645编写
#include<iostream>
#include <string>
#include <vector>
using namespace std;
class Screen
{
public://公有成员
//使用类型别名等价地声明一个类型名字
typedef std::string::size_type pos;
Screen() = default;//因为Screen有另一个构造函数,所以本函数是必须的
//cursor被其类内初始值初始化为0
Screen(pos ht, pos wd, char c)
:height(ht), width(wd), contents(ht* wd, c) {}
char get()const//读取光标处的字符
{
return contents[cursor];//隐式内联
}
inline char get(pos ht, pos wd)const;//显式内联
Screen& move(pos r, pos c);
void some_member()const;
private://私有成员
mutable size_t access_ctr;//即使在一个const对象内也能被修改
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
void Screen::some_member()const
{
++access_ctr;//保存一个计数器
}
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width;//计算行的位置
cursor = row + c;//在行内将光标移动到指定的列
return *this;//以左值的形式返回对象
}
char Screen::get(pos r, pos c)const//在类的内部声明成inline
{
pos row = r * width;//计算行的位置
return contents[row + c];//返回给定列的字符
}
class Window_msg
{
private:
//这个Window_mgr追踪Screen
//默认情况,一个Window包含一个标准尺寸的空白Screen
std::vector<Screen> screens{ Screen(24,80,' ') };
};
int main()
{
Screen myScreen;
char ch = myScreen.get();//调用Screen::get()
ch = myScreen.get(0, 0);//调用Screen::get(pos,pos)
system("pause");
return 0;
}
=====================================================================
第7章 类 246页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
//练习7.23:编写你自己的Screen类。
class Screen
{
public:
/*练习7.24:给你的Screen类添加三个构造函数:一个默认构造函数;另一个构造函数接受
宽和高的值,然后将contents初始化成给定数量的空白;第三个构造函数接受
宽和高的值以及一个字符,该字符作为初始化后屏幕的内容。*/
Screen() = default;
Screen(unsigned ht,unsigned wd)
:height(ht), width(wd), contents(ht*wd, ' ') {}//ht*wd,' '表示初始ht*wd个空格
Screen(unsigned ht,unsigned wd,char c)
:height(ht),width(wd),contents(ht*wd,c){}//ht*wd,c表示初始ht*wd个c的字符
private:
unsigned height = 0, width = 0;
unsigned cursor = 0;
string contents;
};
int main()
{
#ifdef DECLARATION
练习7.25:Screen 能安全地依赖于拷贝和赋值操作的默认版本吗?如果能,为什么?如果不能?为什么?
可以依赖,因为类不需要额外分配对象之外的资源,且string类在合成的构造函数中能正常工作。
练习7.26:将Sales_data::avg_price 定义成内联函数。
inline Sales_data::avg_price() { }
#endif
}
=====================================================================
第7章 类 246页 返回*this的成员函数
=====================================================================
//QQ108201645编写
#include<iostream>
#include <string>
#include <vector>
using namespace std;
class Screen
{
public://公有成员
//使用类型别名等价地声明一个类型名字
typedef std::string::size_type pos;
//Screen() = default;//因为Screen有另一个构造函数,所以本函数是必须的
//cursor被其类内初始值初始化为0
Screen(unsigned ht=0,unsigned wd=0)
:height(ht),width(wd),contents(ht*wd,' '){}
Screen(pos ht, pos wd, char c)
:height(ht), width(wd), contents(ht* wd, c) {}
char get()const//读取光标处的字符
{
return contents[cursor];//隐式内联
}
inline char get(pos ht, pos wd)const;//显式内联
Screen& move(pos r, pos c);
void some_member()const;
Screen& set(char);
Screen& set(pos, pos, char);
//根据对象是const 重载了display函数,调用do_display
Screen& display(std::ostream& os)
{
do_display(os);
return *this;
}
const Screen& display(std::ostream& os)const
{
do_display(os);
return *this;
}
private://私有成员
mutable size_t access_ctr;//即使在一个const对象内也能被修改
//该函数负责显示Screen的内容
void do_display(std::ostream& os)const
{
os << contents;
}
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
void Screen::some_member()const
{
++access_ctr;//保存一个计数器
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;//设置当前光标所在位置的新值
return *this;//将this对象作为左值返回
}
inline Screen& Screen::set(pos r, pos col, char c)
{
contents[r*width+col] = c;//设置给定位置的新值
return *this;//将this对象作为左值返回
}
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width;//计算行的位置
cursor = row + c;//在行内将光标移动到指定的列
return *this;//以左值的形式返回对象
}
char Screen::get(pos r, pos c)const//在类的内部声明成inline
{
pos row = r * width;//计算行的位置
return contents[row + c];//返回给定列的字符
}
class Window_msg
{
private:
//这个Window_mgr追踪Screen
//默认情况,一个Window包含一个标准尺寸的空白Screen
std::vector<Screen> screens{ Screen(24,80,' ') };
};
int main()
{
Screen myScreen(5,3);
char ch = myScreen.get();//调用Screen::get()
ch = myScreen.get(0, 0);//调用Screen::get(pos,pos)
//把光标移动到一个指定的位置,然后设置该位置的字符值
myScreen.move(4,0).set('#');//传入4与0后返回自身再调用Screen::set
#ifdef DECLARATION
//等价于
myScreen.move(4,0);
myScreen.set('#');
//如果move和set返回Screen而不是Screen&,则上述语句的行为将大不相同
Screen temp = myScreen.move(4, 0);//对返回值进行拷贝
temp.set('#');//不会改变myScreen的contents
#endif
//如果display返回常量引用,则调用set将引发失败
myScreen.display(cout).set('*');
const Screen blank(5, 3);
myScreen.set('#').display(cout);//调用非常量版本,this隐式的从指向非常量的指针,转换指向常量的指针
cout<<endl;
blank.display(cout);//调用常量版本
cout<<endl;
system("pause");
return 0;
}
=====================================================================
第7章 类 246页 练习题
=====================================================================
//QQ108201645编写
#include<iostream>
#include <string>
#include <vector>
using namespace std;
class Screen
{
public://公有成员
//使用类型别名等价地声明一个类型名字
typedef std::string::size_type pos;
//Screen() = default;//因为Screen有另一个构造函数,所以本函数是必须的
//cursor被其类内初始值初始化为0
Screen(unsigned ht=0,unsigned wd=0)
:height(ht),width(wd),contents(ht*wd,' '){}
Screen(pos ht, pos wd, char c)
:height(ht), width(wd), contents(ht* wd, c) {}
char get()const//读取光标处的字符
{
return contents[cursor];//隐式内联
}
inline char get(pos ht, pos wd)const;//显式内联
Screen& move(pos r, pos c);
void some_member()const;
Screen& set(char);
Screen& set(pos, pos, char);
//根据对象是const 重载了display函数,调用do_display
Screen& display(std::ostream& os)
{
do_display(os);
return *this;
}
const Screen& display(std::ostream& os)const
{
do_display(os);
return *this;
}
private://私有成员
mutable size_t access_ctr;//即使在一个const对象内也能被修改
//该函数负责显示Screen的内容
void do_display(std::ostream& os)const
{
os << contents;
}
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
void Screen::some_member()const
{
++access_ctr;//保存一个计数器
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;//设置当前光标所在位置的新值
return *this;//将this对象作为左值返回
}
inline Screen& Screen::set(pos r, pos col, char c)
{
contents[r*width+col] = c;//设置给定位置的新值
return *this;//将this对象作为左值返回
}
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width;//计算行的位置
cursor = row + c;//在行内将光标移动到指定的列
return *this;//以左值的形式返回对象
}
char Screen::get(pos r, pos c)const//在类的内部声明成inline
{
pos row = r * width;//计算行的位置
return contents[row + c];//返回给定列的字符
}
class Window_msg
{
private:
//这个Window_mgr追踪Screen
//默认情况,一个Window包含一个标准尺寸的空白Screen
std::vector<Screen> screens{ Screen(24,80,' ') };
};
int main()
{
Screen myScreen(5, 3);
const Screen blank(5, 3);
myScreen.set('#').display(cout);//调用非常量版本,this隐式的从指向非常量的指针,转换指向常量的指针
cout << endl;
blank.display(cout);//调用常量版本
cout<<endl;
system("pause");
return 0;
}
=====================================================================
第7章 类 249页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
class Screen
{
public:
//使用类型别名等价地声明一个类型名字
typedef std::string::size_type pos;
//Screen()=default;//因为Screen有另一个构造函数,所以本函数是必须的
//cursor被其类内初始值,初始化为0
Screen(unsigned ht = 0, unsigned wd = 0)
:height(ht), width(wd), contents(ht*wd, ' '){}
Screen(unsigned ht, unsigned wd, char c)
:height(ht), width(wd), contents(ht*wd, c) {}
char get()const//读取光标处的字符
{
return contents[cursor];//隐式内联
}
inline char get(pos r, pos c)const//显式内联,类的内部声明成inline
{
pos row = r * width;//计算行的位置
return contents[row + c];//返回给定列的字符
}
Screen& move(pos r, pos c);
void some_member()const;
Screen& set(char);
Screen& set(pos, pos, char);
//根据对象是const 重载了display函数,调用do_display
Screen& display(std::ostream& os)
{
do_display(os);
return *this;
}
const Screen& display(std::ostream& os)const
{
do_display(os);
return *this;
}
private:
mutable size_t access_ctr;
void do_display(std::ostream& os)const
{
os << contents;
}
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width;//计算行的位置
cursor = row + c;//在行内将光标移到指定的列
return *this;//以左值的形式返回对象
}
void Screen::some_member()const
{
++access_ctr;//保存一个计数器
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;//设置当前光标所在位置的新值
return *this;//给this对象作为左值返回
}
inline Screen& Screen::set(pos r, pos col, char c)
{
contents[r*width + col] = c;//设置给定位置的新值
return *this;//将this对象作为左值返回
}
class Window_mgr
{
private:
//这个Window_mgr追踪Screen
//默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
std::vector<Screen>screen{ Screen(24,80,' ') };
};
int main()
{
/*练习7.27:给你自己的Screen 类添加move、set 和display 函数,通过执行下面
的代码检验你的类是否正确。*/
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
system("pause");
return 0;
}
=====================================================================
第7章 类 249页 练习题
=====================================================================
//QQ108201645编写(调整一下)
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Screen
{
public:
//使用类型别名等价地声明一个类型名字
typedef std::string::size_type pos;
//Screen()=default;//因为Screen有另一个构造函数,所以本函数是必须的
//cursor被其类内初始值,初始化为0
Screen(unsigned ht = 0, unsigned wd = 0)
:height(ht), width(wd), contents(ht*wd, ' '){}
Screen(unsigned ht, unsigned wd, char c)
:height(ht), width(wd), contents(ht*wd, c) {}
char get()const//读取光标处的字符
{
return contents[cursor];//隐式内联
}
inline char get(pos r, pos c)const//显式内联,类的内部声明成inline
{
pos row = r * width;//计算行的位置
return contents[row + c];//返回给定列的字符
}
Screen& move(pos r, pos c);
void some_member()const;
Screen& set(char);
Screen& set(pos, pos, char);
//根据对象是const 重载了display函数,调用do_display
Screen& display(std::ostream& os)
{
do_display(os);
return *this;
}
const Screen& display(std::ostream& os)const
{
do_display(os);
return *this;
}
private:
mutable size_t access_ctr;
void do_display(std::ostream& os)const
{
os << contents;
}
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width;//计算行的位置
cursor = row + c;//在行内将光标移到指定的列
return *this;//以左值的形式返回对象
}
void Screen::some_member()const
{
++access_ctr;//保存一个计数器
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;//设置当前光标所在位置的新值
return *this;//给this对象作为左值返回
}
inline Screen& Screen::set(pos r, pos col, char c)
{
contents[r*width + col] = c;//设置给定位置的新值
return *this;//将this对象作为左值返回
}
class Window_mgr
{
private:
//这个Window_mgr追踪Screen
//默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
std::vector<Screen>screen{ Screen(24,80,' ') };
};
int main()
{
/*练习7.27:给你自己的Screen 类添加move、set 和display 函数,通过执行下面
的代码检验你的类是否正确。*/
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
system("pause");
return 0;
}
#ifdef DECLARATION
练习7.28:如果move、set和display函数的返回类型不是Screen&而是Screen,
则在上一个练习中将会发生什么?
返回引用函数是左值,意味着这些函数返回的是对象本身,而非对象的副本
如果把一系列操作连接在一起,所有操作都是在同一个对象上执行
move,set,display函数的返回类型都是Screen&,表示我们首先移动光标到(4.0)位置,
然后将这个位置的字符修改为'#',最后输出myScreen的内容
相反,如果move,set,display函数的返回类型改成Screen,则函数都只返回一个临时副本,
不会改变myScreen的值
练习7.29:修改你的Screen 类,令move、set和display函数返回Screen并检
查程序的运行结果,在上一个练习中你的推测正确吗?
未修改输出结果是
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXX#XXXX
修改后输出结果是
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
练习7.30:通过this指针使用成员的做法虽然合法,但是有点多余。讨论显示地使用
指针访问成员的优缺点。
通过this指针访问成员的优点是,可以非常明确地指出访问的是对象的成员,并且可以在成员函数中
使用与数据成员同名的形参,缺点是显的多余,代码不够简洁
#endif
=====================================================================
#include<iostream>
#include <string>
#include<vector>
using namespace std;
class Screen
{
public:
//使用类型别名等价地声明一个类型名字
typedef string::size_type uint_;
//Screen()=default;//因为Screen有另一个构造函数,所以本函数是必须的
//cursor被其类内初始值,初始化为0
Screen() = default;
Screen(unsigned ht, unsigned wd)
:height(ht), width(wd), contents(ht*wd, ' '){}
Screen(unsigned ht, unsigned wd, char c)
:height(ht), width(wd), contents(ht*wd, c){}
char get()const//读取光标处的字符
{
return contents[cursor];//隐式内联
}
inline char get(uint_ r,uint_ c)const//显式内联,类的内部声明成inline
{
uint_ row = r * width;//计算行的位置
return contents[row + c];//返回给定列的字符
}
void some_member()const
{
++access_ctr;//保存一个计数器
}
Screen& move(uint_ r, uint_ c)
{
uint_ row = r * width;//计算行的位置
cursor = row + c;//在行内将光标移到指定的列
return *this;
}
Screen& set(char c)
{
contents[cursor] = c;
return *this;
}
const Screen& set(uint_ r, uint_ col, char c)
{
contents[r*width + col] = c;
return *this;
}
//根据对象是const 重载了display函数,调用do_display
Screen& display(ostream& os)
{
do_display(os);
return *this;
}
const Screen& display(ostream& os)const
{
do_display(os);
return *this;
}
private:
void do_display(ostream& os)const
{
os << contents;
}
mutable size_t access_ctr;
uint_ cursor = 0;
uint_ height = 0, width = 0;
string contents;
};
class Window_mgr
{
private:
//这个Window_mgr追踪Screen
//默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
vector<Screen>screen{ Screen(24,80,' ') };
};
int main()
{
Screen myScreen(6, 6, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.move(5,1).set('a').display(cout);
cout << "\n";
system("pause");
return 0;
}
=====================================================================
第7章 类 249页 前向声明
=====================================================================
//QQ108201645编写
//#include "Sales_data.h"//使用已写过的Sales_data头文件
#include <iostream>
#include <string>
using namespace std;
struct First
{
int memi;
int getMem();
};
struct Second
{
int memi;
int getMem();
};
First obj1;
//Second obj2 = obj1;//错误:两者类型不同
//即使两个类的成员列表完全一样,也是不同的类型
class Screen;//前向声明(从上至下调用)
class Window_mgr
{
public:
Window_mgr(Screen& screen)
:screen_(screen){}
protected:
Screen& screen_;//需要前向声明
};
class Screen
{
public:
Screen(string& buf)
:buf_(buf){}
private:
string buf_;
//例子
};
int main()
{
#ifdef DECLARATION
Sales_data item1;//默认初始化Sales_data类型的对象
class Sales_data item1;//一条等价的声明
#endif
string buf = "abc";
Screen screen(buf);
Window_mgr win(screen);
}
=====================================================================
#include <iostream>
#include <string>
using namespace std;
struct First
{
int memi;
int getMem();
};
struct Second
{
int memi;
int getMem();
};
First obj1;
//Second obj2 = obj1;//错误:两者类型不同
//即使两个类的成员列表完全一样,也是不同的类型
class Screen
{
public:
//使用类型别名等价地声明一个类型名字
typedef string::size_type uint_;
//Screen()=default;//因为Screen有另一个构造函数,所以本函数是必须的
//cursor被其类内初始值,初始化为0
Screen() = default;
Screen(unsigned ht, unsigned wd)
:height(ht), width(wd), contents(ht*wd, ' ') {}
Screen(unsigned ht, unsigned wd, char c)
:height(ht), width(wd), contents(ht*wd, c) {}
char get()const//读取光标处的字符
{
return contents[cursor];//隐式内联
}
inline char get(uint_ r, uint_ c)const//显式内联,类的内部声明成inline
{
uint_ row = r * width;//计算行的位置
return contents[row + c];//返回给定列的字符
}
void some_member()const
{
++access_ctr;//保存一个计数器
}
Screen& move(uint_ r, uint_ c)
{
uint_ row = r * width;//计算行的位置
cursor = row + c;//在行内将光标移到指定的列
return *this;
}
Screen& set(char c)
{
contents[cursor] = c;
return *this;
}
const Screen& set(uint_ r, uint_ col, char c)
{
contents[r*width + col] = c;
return *this;
}
//根据对象是const 重载了display函数,调用do_display
Screen& display(ostream& os)
{
do_display(os);
return *this;
}
const Screen& display(ostream& os)const
{
do_display(os);
return *this;
}
private:
void do_display(ostream& os)const
{
os << contents;
}
mutable size_t access_ctr;
uint_ cursor = 0;
uint_ height = 0, width = 0;
string contents;
};
class Link_screen
{
Screen window;
Link_screen* next;
Link_screen* prev;
};
/*练习7.31:定义一对类X和Y,其中X 包含一个指向 Y 的指针,而Y包含一个类型为
X 的对象。*/
class X;
class Y
{
X *x;//如果创建y则会报错
};
class X
{
Y Y;
};
int main()
{
}
=====================================================================
//保存为Screen.h头文件
#ifndef _SCREEN_H_
#define _SCREEN_H_
#include <string>
#include <vector>
#include <iostream>
using namespace std;
class Window_mgr;
class Screen
{
public:
//Window_mgr的成员可以访问Screen类的私有部分
friend Window_mgr;//友元类
//Window_mgr::clear必须在Screen类之前被声明
friend void Window_mgr::clear(ScreenIndex); //友元函数
#ifdef DECLARATION
1首先定义Window_mgr类, 其中声明clear函数, 但是不能定义它, 在clear
使用Screen的成员之前必须先声明Screen
2接下来定义Screen, 包括对于Screen的友元声明
3最后定义clear, 此时它才可以使用Screen
#endif
//storeOn的ostream的版本能访问Screen对象的私有部分
friend std::ostream& storeOn(std::ostream&, Screen&);
//使用类型别名等价地声明一个类型名字
typedef std::string::size_type uint_;
//Screen()=default;//因为Screen有另一个构造函数,所以本函数是必须的
//cursor被其类内初始值,初始化为0
Screen() = default;
Screen(unsigned ht, unsigned wd)
:height(ht), width(wd), contents(ht*wd, ' ') {}
Screen(unsigned ht, unsigned wd, char c)
:height(ht), width(wd), contents(ht*wd, c) {}
char get()const//读取光标处的字符
{
return contents[cursor];//隐式内联
}
inline char get(uint_ r, uint_ c)const//显式内联,类的内部声明成inline
{
uint_ row = r * width;//计算行的位置
return contents[row + c];//返回给定列的字符
}
void some_member()const
{
++access_ctr;//保存一个计数器
}
Screen& move(uint_ r, uint_ c)
{
uint_ row = r * width;//计算行的位置
cursor = row + c;//在行内将光标移到指定的列
return *this;
}
Screen& set(char c)
{
contents[cursor] = c;
return *this;
}
const Screen& set(uint_ r, uint_ col, char c)
{
contents[r*width + col] = c;
return *this;
}
//根据对象是const 重载了display函数,调用do_display
Screen& display(std::ostream& os)
{
do_display(os);
return *this;
}
const Screen& display(std::ostream& os)const
{
do_display(os);
return *this;
}
uint_ size() const;
private:
void do_display(std::ostream& os)const
{
os << contents;
}
mutable size_t access_ctr;
uint_ cursor = 0;
uint_ height = 0, width = 0;
std::string contents;
};
Window_mgr::Window_mgr() :screens(1, Screen(24, 80, ' ')) {}
void Window_mgr::clear(ScreenIndex i)
{
//s是一个Screen的引用,指向我们想清空的那个屏幕
Screen& s = screens[i];
//将选定的Screen重置为空白
s.contents = string(s.height*s.width, ' ');
}
/*练习7.33:如果我们给Screen 添加一个如下所示的size成员将发生什么情况?如果
出现了问题,请尝试修改它。*/
#ifdef DECLARATION
uint_ Screen::size() const//根据实际的定义改成uint_
{
return height * width;
}
//编译将发生错误,因为pos的类型未知。在pos前添加类名和作用域运算符即可。
#endif
//修改如下
Screen::uint_ Screen::size() const
{
return height * width;
}
#endif
―――――――――――――――――――――――――――――――――――――――
//保存为Window_mgr.h头文件
#ifndef _WINDOW_MGR_H_
#define _WINDOW_MGR_H_
#include <vector>
#include <string>
#include <iostream>
using namespace std;
class Screen;
class BitMap;
//重载storeOn函数
extern std::ostream& stroreOn(std::ostream&, Screen&);
extern BitMap& stroreOn(BitMap&, Screen&);
class Window_mgr
{
public:
Window_mgr();//构造函数放在Screen结尾
//窗口中每个屏幕的编号
using ScreenIndex = std::vector<Screen>::size_type;
//按照编号将指定的Screen重置为空白
void clear(ScreenIndex);
//添加一个新的名为addScreen的函数
//负责向显示器添加一个新的屏幕
//向窗口添加一个Screen,返回它的编号
ScreenIndex addScreen(const Screen&);
//其他成员与之前版本一样
private:
//这个Window_mgr追踪Screen
//默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
std::vector<Screen> screens;// { 1, Screen(24, 80, ' ') };//改成用Window_mgr()构造函数初始化
//{ 1, Screen(24, 80, ' ') }后面这个不能用,用了出错
};
//返回Window_mgr类所在的ScreenIndex类型,再进Window_mgr的作用域
Window_mgr::ScreenIndex Window_mgr::addScreen(const Screen& s)
{
screens.push_back(s);
return screens.size() - 1;
}
#endif
―――――――――――――――――――――――――――――――――――――――
#include "Window_mgr.h"//"Window_mgr.h"是"Screen.h"的友元。所以要在前面
#include "Screen.h"
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{//类的作用域
Screen::uint_ ht = 24, wd = 80;//使用Screen定义公有成员的uint_类型(如果是私有则不能访问)
Screen scr(ht, wd, ' ');
Screen *p = &scr;;
char c = scr.get();//访问scr对象的get成员
c = p->get();//访问p所指向的get成员
}
=====================================================================
第7章 类 255页 类成员声明的名字查找
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
typedef double Money;//Money等价于double的别名
string bal;
class Access
{
public://公有成员
Money balance()//成员函数(使用外层作用域的Money)
{
return bal;//返回bal的值
}
private:
//typedef double Money;//错误,不能重新定义Money
//尽管上面这个typedef double Money编译有的能通过,但还是不能忽略代码有错的事实
Money bal;//私有成员
};
int main()
{
Access a;
cout<<a.balance()<<endl;
system("pause");
return 0;
}
=====================================================================
第7章 类 256页 成员定义中的普通作用域的名字查找
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;
int height;//定义一个名字,稍后将在Screen中使用
class Screen
{
public:
typedef std::string::size_type pos;
void dummy_fcn(pos height)
{
cursor = width * height;//哪个height是那个参数
}
//不建议的写法:成员函数中的名字不应该隐藏同名的成员
/*void Screen::dummy_fcn(pos height)
{
cursor = width * this->height;//成员height
cursor = width * Screen::height;//成员height
}*/
//不建议的写法:不要把成员名字人微言轻参数或其他局部变量使用
/*void Screen::dummy_fcn(pos height)
{
cursor = width * height;//成员height
}*/
//不建议的写法,不要隐藏外层作用域中可能被用到的名字
/*void Screen::dummy_fcn(pos height)
{
cursor = width * ::height;//哪个height是那个全局的?
}*/
private:
pos cursor = 0;
pos height = 0, width = 0;
};
int main()
{
}
=====================================================================
第7章 类 257页 练习题
=====================================================================
//QQ108201645编写
#ifdef DECLARATION
练习7.34:如果我们把第256页Screen类的pos的typedef放在类的最后一行会
发生什么情况?
将会找不到类型pos,导致编译错误。
练习7.35解释下面代码的含义,说明其中的Type和initVal分别使用了哪个定
义。如果代码存在错误,尝试修改它。
typedef string Type;
Type initVal();
class Exercise
{
public:
typedef double Type;
Type setVal(Type);
Type initVal();
private:
int val;
};
Type Exercise::setVal(Type parm)
{
val = parm + initVal();
return val;
}
#endif
#include <iostream>
using namespace std;
typedef string Type;//声明类型别名Type表示string
Type initVal();//声明函数initVal,返回类型是Type(string)
class Exercise//定义一个新类Exercise
{
public:
typedef double Type;//在内层作用域重新声明类型别名Type表示double
Type setVal(Type);//声明setVal,参数和返回值的类型都是Type(double)
Type initVal()//在内层作用域重新声明函数initVal,返回类型是Type(double)
{
val = (int)0.0;//c风格转换int类型
return val;
}
private:
int val;//声明私有数据val
};
//定义函数setVal,此时的Type是外层作用域,改成内层作用域的Type
Exercise::Type Exercise::setVal(Type parm)
{
val = (int)(parm+initVal());//此处使用的是类内initVal函数(c风格转换int类型)
return val;
}
int main()
{
}
=====================================================================
第7章 类 258页 构造函数初始值列表
=====================================================================
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
#include <string>
#include <iostream>
using namespace std;
/*定义在public:说明符之后的成员在整个程序内可被访问public成员定义类的
接口*/
/*定义在private:说明符之后成员只被类的函数访问,但不能被使用
该类的代码访问,private封装了(即隐藏了)类的实现细节*/
//使用class而非struct开始类的定义,只是形式不同,唯一区别就是默认访问权限不同
//没有定义public: protected: private: 的情况下.在struct里都是public
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
//Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
//更改一下默认构造函数
#ifdef DECLARATION
/*Sales_data(const std::string &s)
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)*/
#endif
Sales_data(std::string s=""):bookNo(s){}
/*Sales_data(const std::string &s,unsigned n,double p)
:bookNo(s),units_sold(n),revenue(p*n){}//会产生重定义要注释*/
#ifdef DECLARATION
//更改为260页方式
Sales_data(std::istream&);//构造函数( 可以直接在函数声明的地方为istream&类型的参数设置默认实参cin)
#endif
//添加一个接收方法
Sales_data(std::istream& is)
{
read(is, *this);
}
Sales_data():units_sold(0),revenue(0){}//使用这句后,Sales_data() = default;
//Sales_data的非成员接口函数,但可以访问类的非公有成员或成员函数
//友元函数(friend)不是类的成员或成员函数
/*Sales_data构造函数的一种写法,虽然合法但比较草率:没有使用构造函数初始值*/
#ifdef DECLARATION
/*Sales_data(const string &s, unsigned cnt, double price);*/
//更改为260页的构造方式
#endif
Sales_data(std::string s,unsigned cnt,double rev)
:bookNo(s),units_sold(cnt),revenue(rev*cnt){}
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
private://私有成员
double ave_price()const;
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
#ifdef DECLARATION
//更改为260页的构造方式
/*
Sales_data::Sales_data(const string &s, unsigned cnt, double price)
{
bookNo = s;
units_sold = cnt;
revenue = cnt * price;
}*/
#endif
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
#ifdef DECLARATION
/*
Sales_data::Sales_data(std::istream &is)
{
read(is, *this);//调用std::istream& read(std::istream&, Sales_data&);
}*/
#endif
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
double Sales_data::ave_price()const
{
return units_sold ? revenue / units_sold : 0;
}//等同于下面
#ifdef DECLARATION
double Sales_data::ave_price()const
{
if (units_sold)
return revenue / units_sold;
else
{
return 0;
}
}
#endif//DECLARATION
#endif
―――――――――――――――――――――――――――――――――――――――
#include "Sales_data.h"
#include <iostream>
#include <string>
using namespace std;
class ConstRef
{
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
//错误,ci和ri必须被初始化
#ifdef DECLARATION
//258页
ConstRef::ConstRef(int ii)
{
i = ii;//正确
ci = ii;//错误,不能给const赋值
ri = i;//错误:ri没被初始化
}
#endif
//正确写法
ConstRef::ConstRef(int ii)
:i(ii),ci(ii),ri(ii){}
//如果成员是const、引用、或者属于某种未提供默认构造函数的类类型,我们
//必须通过构造函数初始值列表为这些成员提供初值
class X
{
public:
int i;
int j;
public:
//未定义的,i在j之前被初始化
X(int val)
:j(val), i(j) {}//写成X(int val): j(val), i(val){}更好,用val初始化i与j
//val初始化了j,然后再用j初始化i,实际上,i先被初始化,所以这个初始值效果是用未定义的
//值j初始化i
X(int val,int val1)//写成X(int val): j(val), i(val1){}更好,用val初始化j,val1初始化i
:j(i), i(val) {}//val初始化了i,然后再用i初始化j
};
int main()
{
string foo = "Hello World!";//定义并初始化
string bar;//默认初始化成空的string对象
bar = "Hello World!";//为bar赋一个新的内容
X x(5);
cout<<"x.j="<<x.j<< " x.i="<<x.i<<endl;
X x1(5, 6);
cout << "x1.j=" << x1.j << " x1.i=" << x1.i << endl;
}
=====================================================================
第7章 类 261页 练习题
=====================================================================
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
#include <string>
#include <iostream>
using namespace std;
/*定义在public:说明符之后的成员在整个程序内可被访问public成员定义类的
接口*/
/*定义在private:说明符之后成员只被类的函数访问,但不能被使用
该类的代码访问,private封装了(即隐藏了)类的实现细节*/
//使用class而非struct开始类的定义,只是形式不同,唯一区别就是默认访问权限不同
//没有定义public: protected: private: 的情况下.在struct里都是public
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
//Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
//保留默认构造函数
Sales_data(std::string s = "")
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)*/
/*Sales_data(const std::string &s,unsigned n,double p)
:bookNo(s),units_sold(n),revenue(p*n){}//会产生重定义要注释*/
//添加一个接收方法
Sales_data(std::istream& is)
{
read(is, *this);
}
//与Sales_data(std::string s = "")冲突,多个默认构造函数
//Sales_data():units_sold(0),revenue(0){}//使用这句后,Sales_data() = default;
//Sales_data的非成员接口函数,但可以访问类的非公有成员或成员函数
//友元函数(friend)不是类的成员或成员函数
/*Sales_data构造函数的一种写法,虽然合法但比较草率:没有使用构造函数初始值*/
Sales_data(std::string s,unsigned cnt,double rev)
:bookNo(s),units_sold(cnt),revenue(rev*cnt){}
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
private://私有成员
double ave_price()const;
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
double Sales_data::ave_price()const
{
return units_sold ? revenue / units_sold : 0;
}//等同于下面
#ifdef DECLARATION
double Sales_data::ave_price()const
{
if (units_sold)
return revenue / units_sold;
else
{
return 0;
}
}
#endif//DECLARATION
#endif
―――――――――――――――――――――――――――――――――――――――
#include "Sales_data.h"
#include <iostream>
#include <string>
using namespace std;
struct X
{
//练习7.36下面的初始值是错误的,请找出问题所在并尝试修改它。
X(int i, int j)
:base(i), rem(base%j) {}
//i初始化了base,再为base除以j的余数初始化rem,实际上是从右到左的初始化,base%j里的base是未知数
#ifdef DECLARATION
int rem, base;
#endif
//修改顺序
int base, rem;
};
class Book
{
private:
string Name, ISBN, Author, Publisher;
double Price = 0;
public:
Book() = default;
Book(const string &n, const string&I, double pr, const string&a, const string&p)
{
Name = n;
ISBN = I;
Price = pr;
Author = a;
Publisher = p;
}
friend istream& operator>>(std::istream& is, Book& item)
{
is >> item.Name >> item.ISBN >> item.Author >> item.Publisher;
return is;
}
Book(std::istream& is)
{
is >> *this;
}
};
//也可以选择Tree
class Tree
{
private:
string Name;
unsigned Age = 0;
double height = 0;
public:
Tree() = default;
Tree(const string& n,unsigned a,double h)
:Name(n),Age(a),height(h){}
};
int main()
{
//测试代码
X x(8, 5);
cout << "x.rem = " << x.rem << "\t x.base = " << x.base << endl;
/*练习7.37:使用本节提供的Sales_data类,确定初始化下面的变量时分别使用了哪个构造函数,然后罗
列出每个对象所有的数据成员的值。*/
Sales_data frist_itemm(cin);
Sales_data next;
Sales_data last("9-999-99999-9");
#ifdef DECLARATION
练习7.38:有些情况下我们希望提供cin作为接受istream& 参数的构造函数的默认
实参,请声明这样的构造函数。
Sales_data(std::istream &is = std::cin);
练习7.39:如果接受string的构造函数和接受 istream&的构造函数都使用默认实
参,这种行为合法吗?如果不,为什么?
不合法,因为这样无法确定具体调用哪个构造函数。
;
练习7.40:从下面的抽象概念中选择一个(或者你自己指定一个),思考这样的类需要
哪些数据成员,提供一组合理的构造函数并阐明这样做的原因。
(a) Book(b) Data(c) Employee
(d) Vehicle(e) Object(f) Tree
;
#endif
system("pause");
return 0;
}
=====================================================================
第7章 类 261页 委托构造函数
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
class Sales_data
{
public://公有成员
//非委托构造函数使用对应的实参初始化成员
Sales_data(std::string s, unsigned cnt, double rev)
:bookNo(s), units_sold(cnt), revenue(rev*cnt) {}
#ifdef DECLARATION
Sales_data() :units_sold(0), revenue(0) {}
Sales_data(std::string s = "")
:bookNo(s) {}
Sales_data(std::istream& is)
{
read(is, *this);
}
#endif
//其余构造函数全都委托给另一个构造函数
Sales_data();
Sales_data(std::string s) : Sales_data(s, 0, 0){}
Sales_data(std::istream& is) :Sales_data()
{
read(is, *this);
}
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
private://私有成员
double ave_price()const;
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data::Sales_data():Sales_data("", 0, 0) {}
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
double Sales_data::ave_price()const
{
return units_sold ? revenue / units_sold : 0;
}//等同于下面
#ifdef DECLARATION
double Sales_data::ave_price()const
{
if (units_sold)
return revenue / units_sold;
else
{
return 0;
}
}
#endif
int main()
{
Sales_data data,data1("abc");
Sales_data data2(cin);
print(cout, data1);
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
/*
练习7.41:使用委托构造函数重新编写你的Sales_data 类,给每个构造函数体添加
一条语句,令其一旦执行就打印一条信息。用各种可能的方式分别创建 Sales_data
对象,认真研究每次输出的信息直到你确实理解了委托构造函数的执行顺序。*/
class Sales_data
{
friend std::istream& read(std::istream& is, Sales_data& item);
friend std::ostream& print(std::ostream& os, const Sales_data& item);
public:
Sales_data(const string& book, unsigned num, double sellp, double salep)
:bookNo(book), units_sold(num), sellingprice(sellp), saleprice(salep)
{
if (sellingprice)
discount = saleprice / sellingprice;
cout<<"构造函数接收书号,销售量,原价,实际售价四个信息"<<endl;
}
Sales_data(const string &book) :Sales_data(book, 0, 0, 0)
{
cout<<"该构造函数接受书号信息"<<endl;
}
Sales_data(std::istream& is) :Sales_data()
{
read(is, *this);
cout << "该构造函数接受用户输入的信息" << endl;
}
Sales_data() :Sales_data("", 0, 0, 0)
{
cout<<"该构造函数无须接受任何信息"<<endl;
}
private:
std::string bookNo;//书籍编号,
unsigned units_sold = 0;//销售量
double sellingprice = 0;//原始价格
double saleprice = 0;//实售价格
double discount = 0;//折扣,
};
std::istream& read(std::istream& is, Sales_data& item)
{
is >> item.bookNo >> item.units_sold >> item.sellingprice >>
item.saleprice;
return is;
}
std::ostream& print(std::ostream& os, const Sales_data& item)
{
os<<item.bookNo<<" "<<item.units_sold<<" "<<item.sellingprice<<" "<<
item.saleprice<<endl;
return os;
}
int main()
{
Sales_data first("978-7-121-15535-2", 85, 128, 109);
Sales_data second;
Sales_data third("978-7-121-15535-2");
Sales_data last(cin);
return 0;
}
=====================================================================
//QQ108201645编写
/*
练习7.42:对于你在练习7.40(参见7.5.1节,第261页)中编写的类,确定哪些构造
函数可以使用委托。如果可以的话,编写委托构造函数。如果不可以,从抽象概念列表
中重新选择一个你认为可以使用委托构造函数的,为挑选出的这个概念编写类定义。*/
#include <iostream>
using namespace std;
class Book
{
public:
Book();
Book(string n1, string n2, double p1, string a1, string d1)
: bookName(n1), book_num(n2), book_price(p1), book_author(a1), book_date(d1)
{
cout << "6 parameter constructor.\n";
}
private:
string bookName;
string book_num;
double book_price;
string book_isbn;
string book_author;
string book_date;
};
Book::Book() : Book("", "", 0.0, "", "")
{
cout << "default constructor.\n";
}
int main(int argc, char const * argv[])
{
Book b1;
return 0;
}
=====================================================================
第7章 类 263页 默认构造函数
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
#include "Sales_data.h"
using namespace std;
class NoDefault
{
/*练习7.43:假定有一个名为 NoDefault 的类,它有一个接受 int 的构造函数,但是
没有默认构造函数。定义类 C,C 有一个 NoDefault 类型的成员,定义C 的默认构造
函数。*/
int number;
public:
NoDefault(int n):number(n){}
NoDefault(const std::string&);
//还有其他成员
};
struct A
{
//默认下my_mem是public的
NoDefault my_mem;
};
#ifdef DECLARATION
A a;//错误,不能为A合成构造函数
#endif
struct B
{
B(){}//错误:b_member没有初始值
#ifdef DECLARATION
NoDefault b_member;
#endif
};
class C
{
NoDefault Nmem;
C(int i=0):Nmem(i){}//必须显示调用NoDefault的带参构造函数初始化Nmem
};
int main()
{
Sales_data primer_5th_ed;
#ifdef DECLARATION
Sales_data obj();//正确,定义了一个函数而非对象
if (obj.isbn() == primer_5th_ed.isbn())//错误,obj是一个函数
;
#endif
Sales_data obj;
if (obj.isbn() == primer_5th_ed.isbn())
;
Sales_data obj2;//定义了一个obj2对象而非函数
/*练习7.44:下面这条声明合法吗?如果不,为什么?
vector<NoDefault> vec(10);
不合法,因为NoDefault既没有默认构造函数又不能值初始化。编译出错*/
/*练习7.45:如果在上一个练习中定义的vector的元素类型是C,则声明合法吗?为什么?
合法,因为C有默认构造函数。
练习7.46:下面哪些论断是不正确的?为什么?
(a) 一个类必须至少提供一个构造函数。
(b) 默认构造函数是参数列表为空的构造函数。
(c) 如果对于类来说不存在有意义的默认值,则类不应该提供默认构造函数。
(d) 如果类没有定义默认构造函数,则编译器将为其生成一个并把每个数据成员初始化成相应类型的默认值。
(a)不正确,简单的类可以依靠编译器自动合成
(b)错误,如果某个构造函数包含若干形参,但同时为形参提供了默认实参,则也有默认构造函数的功能
(c)
解1:错误,如果没有默认构造,则编译器需要隐式的使用默认构造时,则无法使用.所以一般情况都应该构建一个默认构造函数
解2:正确, << More Effective C++ >> 中说到:非必要不提供default constructor,
添加无意义的default constructor(提供了无意义的默认值)会影响效率,因为成员函数必须测试字段是否被初始化。
(d)当类没有定义任何构造函数时,才会自动合成构造函数。*/
}
=====================================================================
第7章 类 264页 隐式的类类型转换
=====================================================================
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
#include <string>
#include <iostream>
using namespace std;
/*定义在public:说明符之后的成员在整个程序内可被访问public成员定义类的
接口*/
/*定义在private:说明符之后成员只被类的函数访问,但不能被使用
该类的代码访问,private封装了(即隐藏了)类的实现细节*/
//使用class而非struct开始类的定义,只是形式不同,唯一区别就是默认访问权限不同
//没有定义public: protected: private: 的情况下.在struct里都是public
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
// Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
explicit Sales_data(const std::string &s, unsigned n, double p) :
bookNo(s), units_sold(n), revenue(p*n) {}
/*Sales_data(std::istream& is)
{
read(is, *this);
}*/
//保留默认构造函数
/*explicit */Sales_data(std::string s = "")
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)
//只允许出现在类内声明构造函数时使用explicit
explicit Sales_data(std::istream& is)
{
read(is, *this);
}
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
private://私有成员
double ave_price()const;
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
double Sales_data::ave_price()const
{
return units_sold ? revenue / units_sold : 0;
}
#endif
―――――――――――――――――――――――――――――――――――――――
#include <string>
#include <iostream>
#include "Sales_data.h"
using namespace std;
int main()
{
string null_book = "9-999-99999-9";
//构造一个临时的Sales_data对象
//该对象的units_sold和revenue等于0,bookNo等于null_book
Sales_data item;
item.combine(null_book);
#ifdef DECLARATION
item.combine("9-999-99999-9");
//错误,把"9-999-99999-9"转成string再转成Sales_data
#endif
item.combine(string("9-999-99999-9"));//显示转换成string
item.combine(Sales_data("9-999-99999-9"));//正确.隐式的转换成string,显示的转载成Sales_data
//item.combine(cin);//隐式的把cin转换成Sales_sold.接受一个istream的Sales_data构造函数,读取标准输入创建一个临时的对象并传给combine
//当使用explicit后,上面这条隐式转换将无法通过
Sales_data item1(null_book);//正确,直接初始化
Sales_data item2 = null_book;//错误,不能将explicit的构造函数用于拷贝形式的初始化过程
system("pause");
return 0;
}
=====================================================================
第7章 类 265页 抑制构造函数定义的隐式转换explicit
=====================================================================
//QQ108201645编写
//保存为Sales_data.h头文件
#ifndef _SALES_DATA_H_
#define _SALES_DATA_H_
#include <string>
#include <iostream>
using namespace std;
/*定义在public:说明符之后的成员在整个程序内可被访问public成员定义类的
接口*/
/*定义在private:说明符之后成员只被类的函数访问,但不能被使用
该类的代码访问,private封装了(即隐藏了)类的实现细节*/
//使用class而非struct开始类的定义,只是形式不同,唯一区别就是默认访问权限不同
//没有定义public: protected: private: 的情况下.在struct里都是public
class Sales_data
{
public://公有成员
//新增构造函数(不同的类内初始化方式)
// Sales_data() = default;//默认构造函数可以声明在类类部,也可以作为定义出现在类的外部
explicit Sales_data(const std::string &s, unsigned n, double p) :
bookNo(s), units_sold(n), revenue(p*n) {}
/*Sales_data(std::istream& is)
{
read(is, *this);
}*/
//保留默认构造函数
explicit Sales_data(std::string s = "")
:bookNo(s) {}//bookNo(s)表示构造函数列表初始化(构造函数初始值列表)
//只允许出现在类内声明构造函数时使用explicit
explicit Sales_data(std::istream& is)
{
read(is, *this);
}
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
friend std::istream& operator>>(std::istream& is, Sales_data&);//operator>>重载,支持cin>>对象名接收
friend std::ostream& operator<<(std::ostream& os, const Sales_data&);//operator<<重载,支持cin>>对象名直接输出内容
//之前已有的其它成员
std::string isbn() const;
Sales_data& combine(const Sales_data&);
private://私有成员
double ave_price()const;
std::string bookNo;
unsigned units_sold = 0;//c++11开始支持类体内初始化(变量名译:单独售出)
double revenue = 0.0;//初始化revenue(译:财政收入)
};//这里要加分号
Sales_data add(const Sales_data &lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;//把lhs的数据拷贝给sum
sum.combine(rhs);//把rhs的数据成员加到sum当中
return sum;
}
//定义read和print函数
std::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 << " " << item.ave_price();
return os;
}
//上面两个等同于operator>>与operator<<重载
std::istream& operator>>(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& operator<<(std::ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.ave_price();
return os;
}
std::string Sales_data::isbn() const
{
//return bookNo;//用于返回Sales_data的数据成员
return this->bookNo;//同上
//默认情况this指针指向非常量的常量指针
}
//函数combine设计初衷类型复合赋值运算符+=
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象
}
double Sales_data::ave_price()const
{
return units_sold ? revenue / units_sold : 0;
}
#endif
―――――――――――――――――――――――――――――――――――――――
#include <string>
#include <iostream>
#include "Sales_data.h"
using namespace std;
int main()
{
string null_book = "9-999-99999-9";
//构造一个临时的Sales_data对象
//该对象的units_sold和revenue等于0,bookNo等于null_book
Sales_data item;
//item.combine(null_book);//构造函数是explicit
#ifdef DECLARATION
item.combine("9-999-99999-9");
//错误,把"9-999-99999-9"转成string再转成Sales_data
#endif
//item.combine(string("9-999-99999-9"));//显示转换成string
//item.combine(Sales_data("9-999-99999-9"));//正确.隐式的转换成string,显示的转载成Sales_data
//item.combine(cin);//隐式的把cin转换成Sales_sold.接受一个istream的Sales_data构造函数,读取标准输入创建一个临时的对象并传给combine
//当使用explicit后,上面这条隐式转换将无法通过
//以下都是explicit的构造函数
//Sales_data item1(null_book);//正确,直接初始化
//Sales_data item2 = null_book;//错误,不能将explicit的构造函数用于拷贝形式的初始化过程
item.combine(Sales_data(null_book));//显式构造的Sales_data对象
item.combine(static_cast<Sales_data>(cin));//正确,static_cast可以使用explicit的构造函数
system("pause");
return 0;
}
=====================================================================
第7章 类 266页 练习题
=====================================================================
//QQ108201645编写
#ifdef DECLARATION
练习7.47:说明接受一个string 参数的Sales_data构造函数是否应该是explicit
的,并解释这样做的优缺点。
是否需要从string到Sales_data的转换依赖于我们对用户使用该转换的看法,也就是说要视具体情况而定。
练习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的:
string null_isbn("9-999-99999-9");
Sales_data item1(null_isbn); //创建了一个Sales_data对象,该对象使用null_isbn转换得到的临时对象进行初始化
Sales_data item2("9-999-99999-9"); //创建了一个Sales_data对象,该对象使用字符串字面值转换得到的临时对象进行初始化
如果是explicit的:
string null_isbn("9-999-99999-9");
Sales_data item1(null_isbn); //创建了一个Sales_data对象,该对象使用null_isbn转换得到的临时对象进行初始化
Sales_data item2("9-999-99999-9"); //创建了一个Sales_data对象,该对象使用字符串字面值转换得到的临时对象进行初始化
构造函数是否是explicit的与直接显式定义对象调用特定的构造函数无关,explicit用于在需要一个从一种类型隐式转换到另一种类型时进行转换抑制。
练习7.49:对于combine 函数的三种不同声明,当我们调用i.combine(s) 时分别
发生什么情况?其中 i 是一个 Sales_data,而 s 是一个string对象。
(a) Sales_data &combine(Sales_data); //正确,string隐式转换到Sales_data
(b)Sales_data &combine(Sales_data&); //错误,不能从string转换到Sales_data &
(c)Sales_data &combine(const Sales_data&) const; //正确,string隐式转换为一个临时Sales_data,该Sales_data被绑定到const Sales_data &
练习7.50:确定在你的Person类中是否有一些构造函数应该是 explicit 的。
是否需要explicit是根据具体情况而定的。
练习7.51:vector 将其单参数的构造函数定义成 explicit 的,而string则不是,
你觉得原因何在?
如果vector单参数构造函数不是explicit的,那么对于这样的一个函数void fun(vector<int> v)来说,可以直接以这样的形式进行调用fun(5),这种调用容易引起歧义,无法得知实参5指的是vector的元素个数还是只有一个值为5的元素。而string类型不是一个容器,不存在这样的歧义问题。
#endif
=====================================================================
第7章 类 266页 聚合类
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
#include "Sales_data.h"
using namespace std;
/*聚合类,所有成员都是公有(public)
没有构造函数
没有类内初始值
没有基类与virtual函数*/
struct Data
{
int ival;
string s;
};
Data val1 = { 0,"Anna" };//等价于val1.ival=0;val1.s=string("Anna");
#ifdef DECLARATION
Data val2 = { "Anna",1024 };//错误,不能使用"Anna"初始化ival,不能用1024初始化s
#endif
int main()
{
/*练习7.52:使用2.6.1节(第64页)的 Sales_data 类,解释下面的初始化过程。如
果存在问题,尝试修改它。*/
//Sales_data item ={ "987-0590353403", 25, 15.99 };
Sales_data item { "987-0590353403", 25, 15.99 };
system("pause");
return 0;
}
=====================================================================
第7章 类 266页 constexpr构造函数
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
using namespace std;
#ifdef DECLARATION
数据成员都必须是字面值类型。
类必须至少含有一个constexpr构造函数。
如果一个数据成员含有类内初始值, 则内置类型成员的初始值必须是一条常量表达
式(参见2.4.4节, 第58页); 或者如果成员属于某种类类型, 则初始值必须使用
成员自己的constexpr构造函数。
类必须使用析构函数的默认定义, 该成员负责销毁类的对象(参见7.1.5节, 第239页)。
#endif
class Debug
{
public:
//constexpr构造函数必须初始化所有成员
constexpr Debug(bool b=true)
:hw(b),io(b),other(b){}
constexpr Debug(bool h,bool i,bool o)
:hw(h),io(i),other(o){}
constexpr bool any()const//注意记得在尾部加上const,不然编译错误
{
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;//硬件错误,而不是IO错误
bool io;//IO错误
bool other;//其它错误
};
#ifdef DECLARATION
练习7.53:定义你自己的 Debug。
class Debug
{
public:
constexpr Debug(int ii, double dd) : i(ii), d(dd)
{
}
~Debug() = default;
private:
int i;
double d;
};
练习7.54:Debug中以 set_ 开头的成员应该被声明成 constexpr 吗?如果不,为什么?
不应该,因为set_开头的成员不含有return 语句。
练习7.55:7.5.5节(第266页)的 Data 类是字面值常量类吗?请解释原因。
不是,因为数据成员s是string类型,而string类型不是字面值类型。
#endif
int main()
{
constexpr Debug io_sub(false, true, false);//调试IO
if (io_sub.any())//等价于if(true)
cerr << "pring appropriate error messages" << endl;
constexpr Debug prod(false);//无调试
if (prod.any())//等价于if(false)
cerr<<"print an error message"<<endl;
system("pause");
return 0;
}
=====================================================================
第7章 类 269页 类的静态成员
=====================================================================
//QQ108201645编写
#include <string>
#include <iostream>
using namespace std;
class Account
{
public:
void calculate()
{
amount += amount * interestRate;//成员函数不用通过作用域就可以直接使用静态成员
}
static double rate()
{
return interestRate;
}
static void rate(double);
private:
std::string owner;
double amount;
static double interestRate;
static double initRate() { return 0; }
//period在类体内只能提供了一个且只有一次的初始值
static constexpr int period = 30;//period是常量表达式 ,静态常量表达式可以在类体内进行初始化
double daily_tbl[period];
};
//类外定义静态成员函数不能重复声明static关键字
void Account::rate(double newRate)
{
interestRate = newRate;
}
//注:静态成员要类内声明,类外定义
double Account::interestRate = initRate();
int main()
{
double r;
r = Account::rate();//使用作用域运算符访问静态成员
Account ac1;
Account *ac2 = &ac1;
r = ac1.rate();//通过Account的对象或引用
r = ac2->rate();//通过指向Account对象的指针
system("pause");
return 0;
}
=====================================================================
第7章 类 266页 静态成员与普通成员应用范围
=====================================================================
//QQ108201645编写
#include <string>
#include <vector>
#include <iostream>
using namespace std;
class Bar
{
public:
private:
//静态成员独立于任何对象因此,静态成员可以正常使用
static Bar mem1;//正确.静态成员可以是不完全类型
Bar *mem2;//指针成员可以是不完全类型
// Bar mem3;//错误,数据成员必须是完全类型
};
class Screen
{
public://公有成员
//使用类型别名等价地声明一个类型名字
typedef std::string::size_type pos;
Screen() = default;//因为Screen有另一个构造函数,所以本函数是必须的
//cursor被其类内初始值初始化为0
Screen(pos ht, pos wd, char c)
:height(ht), width(wd), contents(ht* wd, c) {}
char get()const//读取光标处的字符
{
return contents[cursor];//隐式内联
}
inline char get(pos ht, pos wd)const;//显式内联
Screen& move(pos r, pos c);
void some_member()const;
//bkground 表示一个在类中稍后定义的静态成员
Screen &clear(char = bkground);
private://私有成员
static const char bkground;
mutable size_t access_ctr;//即使在一个const对象内也能被修改
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
void Screen::some_member()const
{
++access_ctr;//保存一个计数器
}
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width;//计算行的位置
cursor = row + c;//在行内将光标移动到指定的列
return *this;//以左值的形式返回对象
}
char Screen::get(pos r, pos c)const//在类的内部声明成inline
{
pos row = r * width;//计算行的位置
return contents[row + c];//返回给定列的字符
}
class Window_msg
{
private:
//这个Window_mgr追踪Screen
//默认情况,一个Window包含一个标准尺寸的空白Screen
std::vector<Screen> screens{ Screen(24,80,' ') };
};
int main()
{
system("pause");
return 0;
}
=====================================================================
第7章 类 272页 练习题
=====================================================================
//QQ108201645编写
#ifdef DECLARATION
练习7.56:什么是类的静态成员?它有何优点?静态成员与普通成员有何区别?
在类中使用static关键词修饰的成员。优点是能适用于某些普通成员无法使用的场合。静态成员与普通成员的区别是静态成员属于整个类,而不是属于一个对象,也即一个类只有一份静态成员,而有多份对象。
练习7.57:编写你自己的 Account 类。
class Account
{
public:
int i;
static char c;
};
char Account::c = 'A';
练习7.58:下面的静态数据成员的声明和定义有错误吗?请解释原因。
// example.h
class Example
{
public:
static double rate = 6.5; //错误,非constexpr static数据成员不允许在类内初始化
static const int vecSize = 20; //正确
//另外需要防止下面的声明被解析成函数成员的声明
static vector<double> vec(vecSize); //错误,constexpr static数据成员必须是字面值类型,vector非字面值类型,不允许类内初始化
};
// example.C
#include "example.h"
double Example::rate;//错误,必须给出静态成员的初始值
vector<double> Example::vec;//错误,同上
#endif
=====================================================================