最近在看《C++ primer》,做到了练习7.27的时候,遇到了一些问题,在此记录。

1    先贴代码

1.1    .h代码

  1. #include <cstdlib>  
  2. #include <cstdio>  
  3. #include <string>  
  4. #include <iostream>  
  5.     
  6. using namespace std;  
  7.     
  8. class Screen  
  9. {  
  10.     typedef string::size_type pos;  
  11.     
  12. public:  
  13.     Screen() = default;  
  14.     Screen(pos high, pos wide, char c) : height(high), width(wide), contents(high * wide, c){};  
  15.     inline Screen &set(pos row, pos col, char c);  
  16.     // #5  
  17.     inline Screen &display(ostream &os);  
  18.     // #6  
  19.     inline const Screen &display(ostream &os) const;  
  20.     inline Screen &move(pos row, pos col);  
  21.     
  22. private:  
  23.     pos height = 0, width = 0;  
  24.     pos cursor = 0;  
  25.     mutable string contents;  
  26.     // #1  
  27.     // inline void do_display(ostream &os) const{  
  28.     //     os << contents;  
  29.     // }  
  30.     // #3  
  31.     inline void do_display(ostream &os /*这里的&不能少*/const;  
  32. }; // #4 这里的分号""不能少  
  33.     
  34. //typedef string::size_type pos;并不需要重复定义  
  35. inline Screen &Screen::set(pos row, pos col, char c)  
  36. {  
  37.     contents[(row - 1) * width + (col - 1)] = c;  
  38.     return *this;  
  39. }  
  40. inline Screen &Screen::move(pos row, pos col)  
  41. {  
  42.     cursor = row * width + col;  
  43.     return *this;  
  44. }  
  45. inline Screen &Screen::display(ostream &os)  
  46. {  
  47.     do_display(os);  
  48.     return *this;  
  49. }  
  50. inline const Screen &Screen::display(ostream &os) const  
  51. {  
  52.     do_display(os);  
  53.     return *this;  
  54. }  
  55. // #2  
  56. inline void Screen::do_display(ostream &os) const  
  57. {  
  58.     os << contents;  
  59.     //return *this;  
  60. }  

1.2    .cpp代码

  1. #include "exercise_7_27.h"  
  2.     
  3. using namespace std;  
  4.     
  5. int main()  
  6. {  
  7.     Screen pm1(3, 3, '@');  
  8.     const Screen pm2(3, 3, '$');  
  9.     pm1.display(cout);  
  10.     cout << '\n';  
  11.     pm1.set(3, 1, '#').display(cout);  
  12.     cout << '\n';  
  13.     // #7  
  14.     pm2.display(cout);  
  15.     cout << '\n';  
  16. }  

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

于宁波大学园区图书馆二楼窗边