int main()
{
Sales_data total;
if (cin >> total)
{
Sales_data trans;
while (cin >> trans)
{
if (total.isbn() == trans.isbn())
total += trans;
else
{
cout << total << endl;
total = trans;
}
}
cout << total << endl;
}
else
{
cerr << "No data!" << endl;
return -1;
}
return 0;
}
class Person
{
private:
string strName;
string strAddress;
};
都应该是const的,因为在函数体内只读取数据不作改变
class Person
{
private:
string Name;
string Address;
public:
string getName() const { return Name; }
string getAddress() const { return Address; }
};
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data temp = lhs;
temp.units_sold += rhs.units_sold;
temp.revenue += rhs.revenue;
return temp;
}
istream &read(istream &is, Sales_data &rhs)
{
double price = 0.0;
is >> rhs.bookNo >> rhs.units_sold >> price;
rhs.revenue = price * rhs.units_sold;
return is;
}
ostream &print(ostream &os, const Sales_data &rhs)
{
os << rhs.isbn() << " " << rhs.units_sold << " "
<< rhs.revenue << " " << rhs.avg_price();
return os;
}
因为要修改传入的对象,所以不能设为const,print因为不修改,所以设置为const
istream &read(istream &is, Person &rhs)
{
is >> rhs.Name >> rhs.Address;
return is;
}
ostream &print(ostream &os, Person &rhs)
{
os << rhs.Name << " " << rhs.Address;
return os;
}
data1和data2是否能正确读取
Sales_data() = default;
Sales_data(const string &No) : bookNo(No) {}
Sales_data(const string &No, unsigned n) : bookNo(No), units_sold(n) {}
Sales_data(const string &No, unsigned n, double p) : bookNo(No), units_sold(n), revenue(p) {}
Sales_data(istream &is){ is >> *this; }
Sales_data total(cin);
if (cin)
{
Sales_data trans(cin);
...
Sales_data(const string &No) : bookNo(No), units_sold(n), revenue(0.0) {}
Person() = default;
Person(const string &n) : Name(n) {}
Person(const string &n, const string &add) : Name(n), Address(add) {}
没有位置和次数限定。
构造函数和一部分成员函数定义在public之后
数据成员或作为实现部分的函数则在private后
struct默认访问权限是public
class默认访问权限是private
封装指保护类的成员不被随意访问的能力。一是确保用户代码不会无意间破坏封装对象的状态;二是封装的类的具体实现细节可以随时改变而无需调整用户级别的代码
Name和Address设置为private,构造函数和两个获取信息的函数设为public
非成员函数需要访问类内部时可以声明为友元,可以像类的成员一样访问类的所有数据和函数
但是操作不当就会破坏类的封装性
struct Sales_data
{
friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);
friend ostream &print(ostream &os, const Sales_data &rhs);
friend istream &read(istream &is, Sales_data &rhs);
private:
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
......
class Person
{
friend istream &read(istream &is, Person &rhs);
friend ostream &print(ostream &os, Person &rhs);
private:
string Name;
string Address;
......
class Screen
{
public:
typedef string::size_type pos;
Screen() = default;
Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {}
char get() const { return contents[prob]; }
char get(pos r, pos c) const;
Screen &move(pos r, pos c);
private:
pos height = 0, width = 0;
pos prob = 0;
string contents;
};
inline
char Screen::get(pos r, pos c) const
{
pos row = r * width;
return contents[row + c];
}
Screen &Screen::move(pos r, pos c)
{
pos row = r * width;
prob = row + c;
return *this;
}
Screen() = default;
Screen(pos ht, pos wd) : height(ht), width(wd), contents(ht * wd, ' ') {}
Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {}
含有指针数据成员的类不宜使用默认的拷贝和赋值
但是Screen的4个数据成员都是内置的,所以可以
定义在类内部
double avg_price() const
{
if (units_sold)
return revenue / units_sold;
else
return 0;
}
输出:
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXX#XXXX
不设置为引用只会返回副本,如果改成返回Screen,则上面代码中的move和set都不作改变
通过this指针访问成员可以明确的指出访问的是对象的成员
并且可以在成员函数中使用与数据成员同名的形参
class Y;
class X
{
Y* ptrY;
};
class Y
{
X objX;
};
难点在于结构的组织
必须先定义Window_mgr类,其中声明但不定义clear
接着定义Screen类,其中指明Window_mgr的clear是自己的友元
最后定义clear函数
Screen::pos Screen::size() const
{
return height * width;
}
会出现未定义的标识符,因为会在名字使用之前的声明中找
Type setVal(Type)和Type initVal()的Type都是自己类中的double
Type Exercise::setVal(Type parm)中前者是全局作用域的string,后者是double
会发生错误,因为setVal在类内返回double,定义确返回string。修改如下:
Exercise::Type Exercise::setVal(Type parm)
初始化顺序和数据成员在类中出现的次序有关,所以这里先出现初始化rem再初始化base
但是初始化rem会用到base的值,base又尚未初始化,所以会出错
struct X
{
X(int i, int j) : base(i), rem(base % j) {}
int base, rem;
};
Sales_data next使用Sales_data()
bookNo = "" units_sold = 0 revenue = 0
Sales_data last("9-999-99999-9")使用Sales_data(const string &No) : bookNo(No) {}
bookNo = "9-999-99999-9" units_sold = 0 revenue = 0
Sales_data(istream:: &is = cin) { is >> *this; }
会出二义性错误
Book类,数据成员有书名、作者、价格、ISBN、出版社等
Book() = default;
Book(const string &name, const string &author, const double &price, const string &ISBN, , const string &publish)...
Book(istream &is) { is >> *this; }
class C
{
public:
Nodefault nd;
C(int i = 0) : nd(i) {}
};
不合法。因为NoDefault没有默认构造函数,无法执行默认初始化
合法
(a)错误,可以不提供,此时编译器会自动合成一个默认的构造函数
(b)错误,所有形参都提供了默认实参的构造函数也有默认构造函数的功能
(c)错误,类一般情况下都应该有一个默认构造函数
(d)错误
应该是explicit的。否则可能会在不知情的情况下将string转换成一个Sales_data对象
Sales_data item1(null_isbn)调用Sales_data(const string &No)
Sales_data item1("9-999-99999-9")也是调用Sales_data item1(null_isbn)
构造函数设为explicit也不会影响上面两个对象的构造
(a)可以正常运行
(b)错误,s隐式转换为临时的Sales_data,但是临时的Sales_data不能传递给非常量引用
(c)编译无法通过,因为函数是常量成员函数,违背了combine的性质
(1)rate和vec要初始化必须是静态常量成员
(2)example.c两条语句也是错的,这里必须给出静态成员的初始值