玩命加载中 . . .

7.3-类的其他特性


7.3 类的其他特性

7.3.1 类成员再探

class Screen {
public:
    typedef string::size_type pos;
    // using pos = string::size_type
private:
    pos cursor = 0;
    pos height = 0, width = 0;
    string contents;
};

这里定义了一个screen类,pos是一个别名,类似于unsigned

Screen类的成员函数

class Screen {
public:
    typedef string::size_type pos;
    Screen() = default;
    Screen(pos h, pos w, char c)
        : height(h), width(w), contents(h * w, c) {}
    char get() const { return contents[cursor]; }   // 隐式内联
    inline char get(pos, pos) const;    // 显式内联
    Screen& move(pos, pos);     // 能在之后被设为内联
};

因为已经提供了一个构造函数了,编译器就不会生成默认构造函数,如果还是需要默认构造函数,就需要显式地声明出来

令成员作为内联函数

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

char Screen::get(pos r, pos c) const {
    pos row = r * width;
    return contents[row + c];   // 获取指定位置的字符
}

inline  // 可以在函数的定义处指定inline
Screen& Screen::move(pos r, pos c) {
    pos row = r * width;
    cursor = row + c;   // 移动光标到指定位置
    return *this;
}

重载成员函数

只要成员函数在参数的数量和/或类型上有所区别,就可以实现重载

Screen myscreen;
char ch = myscreen.get();   // 调用 Screen::get()
ch = myscreen.get(0, 0);    // 调用 Screen::get(pos, pos)

可变数据成员

有时会希望修改某个数据成员,即使是在一个const成员函数里面,可以通过在变量声明中加入mutable做到

class Screen {
public:
    void some_member() const;
private:
    mutable size_t access_ctr;  // 即使在一个const对象内也能修改
};
void Screen::some_member() const {
    ++access_ctr;
}

类数据成员的初始值

定义一个窗口管理类,包含一个Screenvector,并增加一个默认初始化元素

class Window_mgr {
private:
    vector<Screen> screens{Screen(24, 80, '*')};
};

当我们提供一个类内初始值时,必须以=或者{}表示

7.3.2 返回*this的成员函数

增加修改当前位置和指定位置字符的成员函数

class Screen {
public:
    Screen& set(char);
    Screen& set(pos, pos, char);
};
inline Screen& Screen::set(char c) {
    contents[cursor] = c;   // 修改当前位置的字符
    return *this;
}

inline Screen& Screen::set(pos r, pos c, char ch) {
    contents[r * width + c] = ch;   // 修改指定位置的字符
    return *this;
}

因为是返回对象本身,所以可以连续调用函数

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

从const成员函数返回*this

可以定义一个display函数输出,可以指定为const,那么返回值就应该是const Screen&,因此不能再去调用非const的函数

Screen myScreen;
myScreen.display(cout).set('*');    // Err

尽管myScreen是个非常量对象,但display返回的是常量引用,所以就不能调用set()对非共有变量进行修改

一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用

基于const的重载

class Screen {
public:
    Screen& display(ostream& os) {
        do_display(os);
        return *this;
    }
    const Screen& display(ostream& os) const {
        do_display(os);
        return *this;
    }
private:
    void do_display(ostream& os) const { os << contents; }
};

在某个对象上调用display时,该对象是否是const决定了应该调用display的哪个版本

Screen myScreen(5, 3, '$');
const Screen blank(5, 3, '&');
myScreen.set('%').display(cout);    // 调用非常量版本
blank.display(cout);    // 调用常量版本

7.3.3 类类型

每个类定义了唯一的类型,对于两个类来说,即使他们的成员完全一样,也是不同的类型

类的声明

可以仅仅声明类而暂时不定义

class Screen;   // 类的声明

在声明之后定义之前是不完全类型

可以定义指向这种类型的指针或引用,也可以声明(不能定义)以不完全类型作为参数或者返回类型的函数

一旦一个类的名字出现后,就被认为是声明过了,因此类允许包含指向它本身的引用或指针

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

7.3.4 友元再探

如果Window_mgr类需要改变Screen对象的值,需要在Screen内将其设为友元

class Screen {
    friend class Window_mgr;
};

这样Window_mgr的成员就能访问Screen类的私有部分

如果一个类指定了友元,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员
class Window_mgr {
public:
    using ScreenIndex = vector<Screen>::size_type;
    void clear(ScreenIndex);
private:
    vector<Screen> screens{Screen(24, 80, '*')};
};

void Window_mgr::clear(ScreenIndex i) { // 要清空的Screen的下标
    Screen& s = screens[i];     // 指向要清空的那个Screen
    s.contents = string(s.height * s.width, ' ');
}

友元关系不存在传递性,如果Window_mgr有自己的友元,那他们并不能访问Screen的成员

令成员函数作为友元

可以只让clear函数作为Screen的友元函数,注意clear必须在Screen类之前被声明,顺序是

  1. 首先声明Screen类,不用定义
  2. 然后定义Window_mgr类,声明clear函数
  3. 再定义Screen,包括对于clear友元声明
  4. 最后定义clear
// 1. 先声明Screen,因为Window_mgr中用到了
class Screen;
// 2. 定义Window_mgr类,声明clear函数
class Window_mgr {
public:
    using ScreenIndex = vector<Screen>::size_type;
    void clear(ScreenIndex);
private:
    vector<Screen> screens;     // 这里就不能调用Screen的构造函数了
};
// 3. 定义Screen,包括对于clear友元声明
class Screen
{
    friend void Window_mgr::clear(ScreenIndex);
};
// 4. 最后定义clear
void Window_mgr::clear(ScreenIndex i) {
    Screen& s = screens[i];     // 指向要清空的那个Screen
    s.contents = string(s.height * s.width, ' ');
}

函数重载和友元

要把重载的函数声明为友元,需要逐个声明

友元声明和作用域

可以在类的内部定义友元函数,但也需要在外部声明使得友元函数可见


文章作者: kunpeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kunpeng !
  目录