最近在看《C++ primer》,做到了练习7.27的时候,遇到了一些问题,在此记录。
1 先贴代码
1.1 .h代码
- #include <cstdlib>
- #include <cstdio>
- #include <string>
- #include <iostream>
- using namespace std;
- class Screen
- {
- typedef string::size_type pos;
- public:
- Screen() = default;
- Screen(pos high, pos wide, char c) : height(high), width(wide), contents(high * wide, c){};
- inline Screen &set(pos row, pos col, char c);
- // #5
- inline Screen &display(ostream &os);
- // #6
- inline const Screen &display(ostream &os) const;
- inline Screen &move(pos row, pos col);
- private:
- pos height = 0, width = 0;
- pos cursor = 0;
- mutable string contents;
- // #1
- // inline void do_display(ostream &os) const{
- // os << contents;
- // }
- // #3
- inline void do_display(ostream &os /*这里的&不能少*/) const;
- }; // #4, 这里的分号";"不能少
- //typedef string::size_type pos;并不需要重复定义
- inline Screen &Screen::set(pos row, pos col, char c)
- {
- contents[(row - 1) * width + (col - 1)] = c;
- return *this;
- }
- inline Screen &Screen::move(pos row, pos col)
- {
- cursor = row * width + col;
- return *this;
- }
- inline Screen &Screen::display(ostream &os)
- {
- do_display(os);
- return *this;
- }
- inline const Screen &Screen::display(ostream &os) const
- {
- do_display(os);
- return *this;
- }
- // #2
- inline void Screen::do_display(ostream &os) const
- {
- os << contents;
- //return *this;
- }
1.2 .cpp代码
- #include "exercise_7_27.h"
- using namespace std;
- int main()
- {
- Screen pm1(3, 3, '@');
- const Screen pm2(3, 3, '$');
- pm1.display(cout);
- cout << '\n';
- pm1.set(3, 1, '#').display(cout);
- cout << '\n';
- // #7
- pm2.display(cout);
- cout << '\n';
- }
2 问题与收获
2.1 类内私有成员可以外部定义
如"#1"和"#2"处的两段对do_display()的定义,在类内声明并定义private成员函数是可以的,同样在类内声明,随后在类外定义也是可以的。
2.2 std::ostream os和std::ostream &os引起的错误
在"#3"处的代码,可以看到形参列表中有一个std::ostream &os,这里是指将输出流的引用传入了函数。但是第一次写程序的时候忘记加这个&了,而是变成了输出流的拷贝来使用,出现了如下的错误:
use of deleted function 'std::basic_ostream<_CharT, _Traits>::basic_ostream(const std::basic_ostream<_CharT, _Traits>&)
2.3 类的定义域后没加分号
在"#4"处的大括号结尾处,如果程序没有在此结束,则strut/class{}的后面需要加分号";",不然会报错,如下:
expected initializer before '&' token
但是回头审查代码,这个分号是在第32行没有加的,却提醒我35行的Screen &Screen::move(…)的引用符号出现了问题,真是费解。
2.4 类内成员函数关于const修饰引起的重载
可以看到在"#5"和"#6"的地方定义了两次display()这个函数,主要的区别是对返回的类型的限定不同,都是返回函数指针的函数,但是第一个是返回的非常量类型的(*this),第二个却是常量类型的。
具体的调用重载区别可以看.cpp文件内的"#7"部分,当在vscode 的IDE下进行编写的时候,对于非常量的Screen对象pm1,当你敲下"."的时候,所有在public中定义的成员都是可见的;但是常量的对象pm2只能看到重载的const Screen &Screen::display() const{…}。
2.5 前后const的限定作用
需要特别注意的是,平时虽然后面的const并不影响返回对象的常量属性,只是影响成员函数对成员的可操作性。但是当返回的对象类型是函数类型的时候,如果后面加了const限定符,则调用该成员函数的时候,this指针指向的对象就是常量。
举个例子,Screen &Screen::display() {…}和const Screen &Screen::display() const{…}中都调用了inline void Screen::do_display(ostream &os) const {…},但是不论是第一个display()还是第二个,当调用do_display() const {…}的时候,this指针指向的对象都被隐式地转化为了常量对象,当引用结束后返回的this仍然是调用之前的this类型。
所以可以推导出如果只有前面的const,那么在调用第二个display()函数的时候,返回的this指针是非常量的,这和声明的返回类型不符。但是只有后面的const的时候,两个display()函数从形参到返回类型都是一样的,就出现了重载的二义性问题,编译同样无法通过。
2021.4.18 13:31
于宁波大学园区图书馆二楼窗边