【c++ primer, 5e】类的其他特性
1、Defining a Type Member:
#include <iostream> #include <string> using namespace std; class Screen { public: using pos = string::size_type; /* 这就是“类型成员”,必须先定义后使用(p232) 等价声明: typedef string::size_type pos; string::size_type 一般是 unsigned int */ private: pos cursor = 0; // 光标的位置 pos height = 0, width = 0; // 屏幕的高和宽 string contents; // 保存内容 }; int main() { return 0; }
2、Member Functions of class Screen。
class Screen { public: using pos = string::size_type; Screen() = default; Screen(pos ht, pos wd, char c): height(ht), width(wd), contents(ht * wd, c) {} char get() const { return contents[cursor]; } // implicitly inline inline char get(pos ht, pos wd) const; // explicitly inline Screen &move(pos r, pos c); private: pos cursor = 0; pos height = 0, width = 0; string contents; };
3、Making Members inline
规模小的成员函数适合被声明为inline,例如上面的构造器和其中一个get函数默认是内联的。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 { pos row = r * width; return contents[row+c]; }
需要注意的是内联函数应当和相应的类定义在同一个头文件中。
4、Overloading Member Functions
Screen myscreen; char ch = myscreen.get(); // calls Screen::get() ch = myscreen.get(0,0); // calls Screen::get(pos,pos)
5、mutable Data Members
极少的一种情况(例如记录const函数被调用的次数),我们希望在const成员函数中修改类的数据成员。(正常情况下是做不到的,const函数不能修改数据成员)这个时候,可以将变量声明成mutable来做到这一点。
public: void some_member() const { ++access_ctr; } private: size_t access_ctr;
这种情况无法通过编译:
prog1.cpp: In member function 'void Screen::some_member() const': prog1.cpp:20:5: error: increment of member 'Screen::access_ctr' in read-only object ++access_ctr;
把access_cstr声明成mutable即可:
mutable size_t access_ctr;
6、Initializers for Data Members of Class Type
class Window_mgr { private: vector<Screen> screens{Screen(24, 80, ' ')}; };
类内初始值必须是等号形式或者花括号形式。
7.23 & 7.24
#include <cstdio> #include <iostream> #include <string> #include <cstring> #include <vector> using namespace std; class Screen { public: using pos = string::size_type; // constructors 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) {} // Member Functions char get() const { return contents[cursor]; } inline char get(pos ht, pos wd) const; Screen &move(pos r, pos c); private: pos cursor = 0; pos height = 0, width = 0; string contents; }; 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 { pos row = r * width; return contents[row+c]; } class Window_mgr { private: vector<Screen> screens{Screen(24, 80, ' ')}; }; int main() { Screen myscreen; char ch = myscreen.get(); // calls Screen::get() ch = myscreen.get(0,0); // calls Screen::get(pos,pos) return 0; }
7.25
可以,Screen类并没有涉及动态内存分配,仅包含基本数据类型、string成员,拷贝、赋值、销毁的默认合成版本可以正常工作。
7.26
在原函数的类外定义加上关键字inline即可。
// if move returns Screen not Screen& Screen temp = myScreen.move(4,0); // the return value would be copied temp.set('#'); // the contents inside myScreen would be unchanged
if move returns Screen&:
// move the cursor to a given position, and set that character myScreen.move(4,0).set('#');
1、Returning *this from a const Member Function
A const member function that returns *this as a reference should have a
return type that is a reference to const
2、Overloading Based on const
class Screen { public: // display overloaded on whether the object is const or not Screen &display(std::ostream &os) { do_display(os); return *this; } const Screen &display(std::ostream &os) const { do_display(os); return *this; } private: // function to do the work of displaying a Screen void do_display(std::ostream &os) const {os << contents;} // other members as before };
When we call display on an object, whether that object is const determines which version of display is called:
Screen myScreen(5,3); const Screen blank(5, 3); myScreen.set('#').display(cout); // calls non const version blank.display(cout); // calls const version
7.27
#include <cstdio> #include <iostream> #include <string> #include <cstring> #include <vector> using namespace std; class Screen { public: using pos = string::size_type; // constructors 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) {} // Member Functions char get() const { return contents[cursor]; } inline char get(pos ht, pos wd) const; Screen &move(pos r, pos c); Screen &set(char); Screen &set(pos, pos, char); Screen &display(ostream &os) { do_display(os); return *this; } const Screen &display(ostream &os) const { do_display(os); return *this; } private: pos cursor = 0; pos height = 0, width = 0; string contents; void do_display(std::ostream &os) const { os << contents; } }; 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 { pos row = r * width; return contents[row+c]; } inline Screen &Screen::set(char c) { contents[cursor] = c; return *this; } inline Screen &Screen::set(pos r, pos col, char ch) { contents[r*width + col] = ch; return *this; } class Window_mgr { private: vector<Screen> screens{Screen(24, 80, ' ')}; }; int main() { // Screen myscreen; // char ch = myscreen.get(); // calls Screen::get() // ch = myscreen.get(0,0); // calls Screen::get(pos,pos) // return 0; // Screen myScreen(5, 3); // const Screen blank(5, 3); // myScreen.set('#').display(cout); // blank.display(cout); Screen myScreen(5, 5, 'X'); myScreen.move(4,0).set('#').display(cout); cout << '\n'; myScreen.display(cout); cout << '\n'; return 0; } // output: // XXXXXXXXXXXXXXXXXXXX#XXXX // XXXXXXXXXXXXXXXXXXXX#XXXX
7.28
move将返回一个副本,对myScreen的后续操作不会被改变myScreen本身,而是myScrenn的副本。
7.29
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
7.30
优点:在某些情况下可以区分成员和参数,例如:this->name = name;相比于f(string name): name(name) {}可以添加一些参数检查。
缺点:多数情况下是多余的,例如:f(string nm) nm =name;....
一个类的成员类型不能是该类自己。
7.31
一个典型的不得不用前置声明的例子。
class Y; // forward declaration, Y is an incomplete type. class X { Y* pointer; }; class Y { X object; }; int main() { X x; Y y; return 0; }