C++Primer第五版 第7章 类 7.3类的其他特性 练习

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值