C++Primer第五版 第7章 类 7.3类的其他特性 练习
7.3 类的其他特性
7.3.1 类成员再探
7.23 编写你自己的Screen类
7.24 给你的Screen类添加三个构造函数:一个默认构造函数;另一个构造函数接受宽和高的值,然后将content初始化成给定数量的空白;第三个构造函数接受宽和高的值以及一个字符,该字符作为初始化之后屏幕的内容。
class Screen{
public:
typedef string::size_type pos;
Screen() = default;// 1
Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, ' ') {}// 2
Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c) {}// 3
char get() const { //读取光标处的字符
return contents[cursor]; //隐式内联
}
inline char get(pos ht, pos wd) const ; //显式内联
Screen &move(pos r, pos c); //能在之后被设为内联
private:
string contents;
pos cursor = 0;//光标位置
pos height = 0;//屏幕的高
pos width = 0;//屏幕的宽
};
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]; //返回给定列的字符
}
7.25 Screen能安全地依赖于拷贝和赋值操作的默认版本吗?如果能,为什么?如果不能,为什么?
答:能,Screen中的数据类型只有string和内置类型,可以依赖于默认版本(可见7.1.5)
7.26 将Sales_data::avg_price定义成内联函数。
答:只需要在avg_price(类内)声明处或(和)(类外)定义处加上inline关键字即可
class Sales_data{
friend Sales_data add(const Sales_data&, const Sales_data&);
friend ostream &print(ostream&, const Sales_data&);
friend istream &read(istream&, Sales_data&);
public:
Sales_data() = default;
Sales_data(const string &s);
Sales_data(const string &s, unsigned n, double p);
Sales_data(istream &is){
read(is, *this);
}
string isbn() const {return bookNo;}
Sales_data& combine(const Sales_data&);
private:
double avg_price() const;
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
inline
double Sales_data::avg_price()const{//常量成员函数,不会改变Sales_data对象的值
if(units_sold)
return revenue/units_sold;
else
return 0;
}
7.3.2 返回*this的成员函数
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";
ex7_27.h
class Screen{
public:
typedef string::size_type pos;//自定义某种类型在类中的别名
Screen() = default;// 1
Screen(pos ht, pos wd ):height(ht), width(wd), contents(ht*wd, ' ') {}// 2
Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c) {}// 3
Screen &set(char);//设置光标所在位置的字符
Screen &set(pos, pos, char);//设置其他任一给定位置的字符
char get() const { //读取光标处的字符
return contents[cursor]; //隐式内联
}
inline char get(pos ht, pos wd) const ;
Screen &move(pos r, pos c);
//根据对象是否是const重载了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;}//显示Screen的内容
string contents;
pos cursor = 0;//光标位置
pos height = 0;//屏幕的高
pos width = 0;//屏幕的宽
};
ex7_27.cpp
inline //可以在函数的定义处指定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]; //返回给定列的字符
}
inline Screen& Screen::set(char c){
contents[cursor] = c;//设置当前光标所在位置的新值
return *this;//将this对象作为左值返回
}
inline Screen& Screen::set(pos r, pos col, char ch){
contents[r*width + col] = ch;//设置给定位置的新值
return *this;//将this对象作为左值返回
}
void test(){
cout << "******ex7_27******" << endl;
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
7.28 如果move、set和display函数的返回类型不是Screen& 而是Screen,则在上一个练习中奖会发生什么?
答:当返回类型是Screen时,move、set和display函数返回的是对象的副本,myScreen.move(4, 0).set(‘#’).display(cout);表达式中,move的返回值是*this的副本,调用set只能改变临时副本,而不能改变myScreen的值。
7.29 修改你的Screen 类,令move、set和display函数返回Screen并检查程序的运行结果,在上一个练习中你的推测正确吗?
7.30 通过this指针使用成员的做法虽然合法,但是有点多余。讨论显示使用指针访问成员的优缺点。
答:优点:更明确,减少误读的可能性;可以使用名称与成员名称相同的形参。
void setAddr(const std::string &addr) { this->addr = addr; }
缺点:冗余代码增加。std::string getAddr() const { return this->addr; } // unnecessary
7.3.3 类类型
练习7.31 定义一对类X 和Y,其中X 包含一个指向 Y 的指针,而Y 包含一个类型为 X 的对象。
class Y;
class X {
Y* y = nullptr;
};
class Y {
X x;
};
7.3.4 友元再探
练习7.32 定义你自己的Screen 和 Window_mgr,其中clear是Window_mgr的成员,是Screen的友元。
class Screen;
class Window_mgr {
public:
using ScreenIndex = std::vector<Screen>::size_type;
inline void clear(ScreenIndex);
private:
std::vector<Screen> screens;
};
class Screen {
friend void Window_mgr::clear(ScreenIndex);
public:
using pos = std::string::size_type;
Screen() = default; // 1
Screen(pos ht, pos wd):height(ht), width(wd), contents(ht*wd, ' '){ } // 2
Screen(pos ht, pos wd, char c):height(ht), width(wd), contents(ht*wd, c){ } // 3
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r*width+c]; }
inline Screen& move(pos r, pos c);
inline Screen& set(char c);
inline Screen& set(pos r, pos c, char ch);
const Screen& display(std::ostream &os) const { do_display(os); return *this; }
Screen& display(std::ostream &os) { do_display(os); return *this; }
private:
void do_display(std::ostream &os) const { os << contents; }
private:
pos cursor = 0;
pos height = 0;
pos width = 0;
std::string contents;
};
inline void Window_mgr::clear(ScreenIndex i)
{
if (i >= screens.size()) return; // judge for out_of_range.
Screen &s = screens[i];
s.contents = std::string(s.height * s.width, ' ');
}
inline Screen& Screen::move(pos r, pos c)
{
cursor = r*width + c;
return *this;
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen& Screen::set(pos r, pos c, char ch)
{
contents[r*width+c] = ch;
return *this;
}