7.3 类的其他特性

类型成员

class Screen  
{  
public:  
    typedef std::string::size_type pos;  
private:  
    pos cursor = 0;  
    pos height = 0, width = 0;  
    std::string contents;  
};
  • 类型成员同样存在访问限制,可以是public或private的
  • 用来定义类型的成员必须先定义后使用。因此,类型成员通常出现在类开始的地方
  • 增加类型成员有两个好处,隐藏细节、一改全改
  • 也可以使用等价的using声明

构造函数

Screen类用来表示我们熟悉的命令行窗口,想一想命令行窗口有哪些属性

  • 窗口的大小(宽、高)
  • 光标的位置
  • 窗口中需要显示的内容

把Screen类的数据成员和命令行窗口的属性一一对应后,会更好的理解Screen类

class Screen  
{  
public:  
    Screen() = default;  
    Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {}  
};

为了方便用户在创建时指明窗口的属性值,我们为Screen类添加了一个构造函数,它接受三个参数,分别表示窗口的宽、高和默认显示的内容

注意到构造函数中并没有为cursor指定初始值,它将使用类内初始值

内联函数

定义在类内部的成员函数是自动inline的

class Screen  
{  
public:  
    char get() const { return contents[cursor]; }  
    inline char get(pos ht, pos wd) const;  
    Screen &move(pos r, pos c);  
};
  • 无参的get函数是隐式内联的
  • 带参的get函数是显式内联的
  • move函数仍有机会在定义时添加inline关键字修饰

不需要在函数声明和定义的地方同时说明inline

inline成员函数也应该与相应的类定义在同一个头文件中

可变数据成员

我们知道在const成员函数内,无法修改数据成员的值,但有的时候确实需要。

可以用可变数据成员来解决在const成员函数中无法修改数据成员的问题

在数据成员的声明中加入mutable关键字即可将其声明为可变数据成员

class Screen  
{  
public:  
    void some_member() const;  
private:  
    mutable size_t access_ctr;  
};

一个可变数据成员永远不会是const,即使它是const对象的成员。

void Screen::some_member() const  
{  
    ++access_ctr;  
}

类内初始值

当我们为类的数据成员提供类内初始值时,必须以=或者花括号表示。

不能用()表示,因为可能会出现这样的情况:

class Widget 
{
private: 
  typedef int x;
  int z(x);
};

这样的话,就会变为函数声明。

有关初始化的内容,如{} () = 等可以参考这篇文章:https://zhuanlan.zhihu.com/p/21102748

返回*this的成员函数

class Screen  
{  
public:  
    Screen &set(char);  
    Screen &move(pos r, pos c);  
};  

inline Screen &Screen::set(char c)  
{  
    contents[cursor] = c;  
    return *this;  
}

需要注意的是,set和move的返回值类型都是Screen对象的引用,意味着函数返回的是对象本身而非对象的副本

myScreen.move(4,0).set('#');

这样才能保证类似的连续操作是在同一个对象上执行的。

如果set和move返回Screen而非Screen&,则返回值将是*this的副本。上面的连续调用等价于

Screen temp = myScreen.move(4,0);  
temp.set('#');

从const成员函数返回*this

如果成员函数是const的,那么它的this指针将是一个指向const的指针

而*this是const对象

如果成员函数返回*this,根据赋值规则,返回值类型应该是常量引用,即const Sales_data&

基于const的重载

通过区分成员函数是否是const的,我们可以对其进行重载

Screen &display(std::ostream &os){}  
const Screen &display(std::ostream &os) const {}

两个display成员函数构成重载,这和this指针有关

类类型

注意区分类和对象

  • 类用来定义一类事物
  • 对象用来表示该类事物的一个具体实例

比如定义了Person类,可以抽象出人类共有的特征作为Person类的成员,比如眼睛、手、脚作为数据成员,行走、吃饭、睡觉作为函数成员等,那么具体到某一个人就是类的对象了

Sales_data item1;  
class Sales_data item1;

以上两种方式都可以用来创建对象(第2种方式是由c语言继承而来)

类的声明

和函数类似,我们也可以仅声明类而暂时不定义它,这种声明称作前向声明

class Screen;

这个时候,我们只知道Screen是一个类类型,但是不知道它有哪些成员,因此使用的场景是有限制的。只能在类的级别使用,不能涉及成员操作。

一旦一个类的名字出现后,它就被认为是声明过了(但尚未定义),因此允许包含指向它自身类型的引用或指针

class Link_screen  
{  
    Screen window;  
    Link_screen *next;  
    Link_screen *prev;  
};

友元再探

类除了可以把普通函数作为友元外,还可以

  • 把其他的类定义成友元
  • 把其他类的成员函数定义成友元

令类作为友元

class Screen  
{  
    friend class Window_mgr;  
};

作出声明后,Window_mgr类的成员函数将可以访问Screen类的所有成员。

void Window_mgr::clear(ScreenIndex i)  
{  
    Screen &s = screens[i];  
    s.contents = string(s.height * s.width, ' ');  
}

令成员函数作为友元

class Screen  
{  
    friend void Window_mgr::clear(ScreenIndex);  
};

声明本身很简单,但是必须组织好程序的结构以满足依赖关系

  • 首先定义Window_mgr类,其中声明clear函数,但是不能定义它。在clear使用Screen的成员之前必须先声明Screen
  • 接下来定义Screen,包括对于clear的友元声明
  • 最后定义clear,此时它才可以使用Screen的成员

 参考P252。

猜你喜欢

转载自blog.csdn.net/zpznba/article/details/84827233
7.3