这篇接着First Part通过构造Screen和Window_mgr介绍类的其他特性.
首先介绍要构造的Screen和Window_mgr这两个类:
- Screen表示显示器的一个窗口
- 类型成员包括:
- 代表string::size_type类型的pos成员
- 数据成员包括:
- string类型的contents成员, 存储显示的内容.
- 三个string::size_type类型的cursor height width成员, 分别表示光标位置, 屏幕的高和宽.
- 可变数据成员包括:
- size_t类型的access_ctr成员, 表示每个Screen成员函数被调用的次数.
成员函数包括:
- get函数, 读取给定位置字符.
- set函数, 设置给定位置字符.
- display函数, 打印Screen的内容.
- move函数, 移动光标.
Window_mgr表示管理窗口的类
- 数据成员包括:
- Screen vector类型的screens成员, 表示一个屏幕的窗口组.
- size_type类型的ScreenIndex成员, 表示屏幕中每个窗口的编号.
- 成员函数包括:
clear函数, 清除屏幕中指定编号的窗口.
class Screen
{
friend class Window_mgr; // 类间友元声明
// friend void Window_mgr::clear(ScreenIndex);
public:
typedef std::string::size_type pos; // 类型成员
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
mutable size_t access_ctr; // 可变数据成员
public:
Screen() = default;
Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {}
// 成员函数重载
char get() const{ return contents[cursor]; }
inline char get(pos r, pos c) const;
Screen &move(pos r, pos c);
// 基于const的重载
Screen &display(std::ostream &os)
{
do_display(os);
return *this;
}
const Screen &display(std::ostream &os) const
{
do_display(os);
return *this;
}
private:
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
{
public:
using ScreenIndex = std::vector<Screen>::size_type; // 窗口中每个屏幕的编号
void clear(ScreenIndex); // 清除屏幕
ScreenIndex addscreen(const Screen&);
private:
std::vector<Screen> screens{Screen(24, 80, ' ')}; // 窗口中的屏幕
}
void Window_mgr::clear(ScreenIndex i)
{
Screen &s = screens[i];
s.contents = string(s.height * s.width, ' '); // 访问了Screen的私有成员
}
Window_mgr::ScreenIndex Window_mgr::addscreen(const Screen &s)
{
screens.push_back(s);
return screens.size() - 1;
}
- 类型成员
一个类不仅可以有数据成员和成员函数, 还可以包含类型成员. 类型成员实际上就是在类内部定义的类型别名, 使用类型成员的目的在于隐藏数据成员的类型. 以下两条语句都可以定义类型成员:
typedef std::string::size_type pos;
using pos = std::string::size_type
注意:
1. 类型成员受访问限制符的限制
2. 类型成员必须先定义后使用, 建议将类型成员定义至于类首
- 内联函数
一些较小的而又频繁使用的函数可以作为内联函数, 所谓内联函数指的是在编译时函数调用的位置会被自动替换为函数的实现, 类似于宏.
内联函数的声明有如下方式:
- 类内定义的成员函数自动inline,
- 类内声明成员函数时加上inline关键字
- 类外定义函数时加上inline关键字
注意: inline函数应该与相应的类定义在同一个头文件中
- 可变数据成员
可变数据成员应用在, 我们需要一种数据成员, 即使在const成员函数内, 我们仍然需要改变它. 那么可以利用mutable
关键字定义一个永远不会为const的可变数据成员.
- 基于const的重载
对于返回 *this 的成员函数来说, 如果成员函数为const的, 那么this指针为const的, 解引后的对象const的; 如果成员函数为普通函数, 那么this指针为普通指针, 解引后的对象为普通对象. 在dispaly中, 由于display函数不会改变对象的内容, 所以我们希望dispaly是const的, 此时display返回的是一个const对象的引用, 但是我们又希望通过返回的引用调用一个非const成员函数(set()), 显然不可能, 所以需要对display函数进行基于const的重载, 由调用display的对象是否为const来决定调用const还是非const版本的display.
- 类间友元关系
类间友元关系应用在一个类需要访问另一个类的私有成员(数据成员 成员函数)的情景下. 更具体的, 我们可以将类A的成员函数声明为另一个类B的友元函数. 但此时该成员函数要在类B前声明.
.
- 使用友元关键点
- 可以将一个类外定义的非成员函数声明为一个类的友元
- 可以将一个类的成员函数声明为另一个类的友元
- 可以将一个类声明为另一个类的友元类
- 声明为一个类的友元函数的成员函数必须在该类前声明
- 重载函数名相同但仍然是不同的函数, 必要时需要对每一个重载函数声明友元
- 友元声明不受类的访问控制影响
- 即使友元函数在类内定义了也必须在类外声明该函数, 因为友元声明并不是真正的函数声明
- 类外定义的成员函数名前为什么需要类名和作用域运算符?
类中的名字只对类内可见, 使用这些名字时需要指明具体的类, 可以通过类对象, 类对象的指针, 类对象的引用通过成员访问运算符指明, 也可以通过作用域运算符指明.
成员函数函数名前指明类名后, 类中的名字对成员函数参数列表和函数体都可见, 但是返回参数仍不可见, 所以返回参数前也需要类名
- 委托构造函数
一个委托构造函数使用它所属类的其他构造函数执行初始化过程.
class Sales_data
{
public:
// 非委托构造函数, 使用初始化列表初始化
Sales_data(std::string str, unsigned cnt, double price)
: bookNo(str), units_sold(cnt), revenue(cnt * price) {}
// 委托构造函数
Sales_data()
: Sales_data(" ", 0, 0) {}
Sales_data(std::string str)
: Sales_data(str, 0, 0) {}
Sales_data(std::istream &is)
: Sales_data() { read(is, *this); }
//...
}
委托构造函数的执行顺序是: 接受委托的构造函数的初始化列表->接受委托的构造函数的函数体->委托函数的函数体
.