C++ Primer第七章③

C++ Primer

第七章 类

类的其他特性

前面通过Sales_data类我们已经了解了很多类的知识了,但是作为C++这么知名的复杂的语言,肯定还有更多东西我们还没了解,接下来就来介绍这些特性:类型成员、类的成员的类内初始值、可变数据成员、内联成员函数、从成员函数返回*this、如何定义并使用类类型以及友元类的更多知识。

类成员再探

我们现在先来定义一个类,Screen屏幕类:

class Screen
{
public:
    typedef string::size_type pos; //类型别名,别忘了啊
    Screen() = default //强制生成默认构造函数

    //自定义构造函数,别忘了这个初始化方式
    Screen(pos ht, pos wd, char c): height(ht), width(wd), contents(ht*wd, c){}

    //定义在类内部的成员函数都是隐式的内联函数
    char get() const //别忘了这边的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; //返回左值对象
}
//重载get函数
char Screen::get(pos r, pos c) const
{
    pos row = r * width;
    return contents[row + c];
}
//其实在声明和定义处都写inline也可以,推荐在类外面写,更易理解

以上也得出了一个结论,要想写非内联的成员函数,只能在类内声明在类外不用inline定义。

强行可变数据成员

这个应用很少,我觉得这个设定有点奇怪,理解倒是好理解的,你自己看代码吧:

class Screen
{
public:
    void some_member() const; //成员函数声明

private:
    mutable size_t access_ctr; //mutable即使在const对象内也能被修改
}

void Screen::some_member() const
{
    ++access_ctr; //你看,可以改变吧
};
类数据成员的初始值

在定义好Screen类后,我们要来定义一个窗口管理类Window_mgr,用它来表示显示器上的多个Screen(因为屏幕上可以出现多个窗口),这个类应该包含一个Screen的vector。在默认情况下,我们希望这个类有一个默认初始化的Screen,这时候就可以用到类内初始值了:

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

过程是这样的:我们用了列表初始化去初始screens这个Window_mgr类的类内成员变量,在列表初始化内容中,我们调用了Screen类的构造函数去实例化一个匿名对象。


==接下来要搞个***了,我给Screen类添加一些函数以及调用它们的代码,你来判断有没有问题==

//定义,上面的寒素也都包括在里面,我不重复了
class Screen
{
public:
    Screen &set(char);
    const Screen &display();
};
inline Screen &Screen::set(char c)
{
    contents[cursor] = c;
    return *this;
}
const Screen &display()
{
    cout << this->contents << endl; 
    return *this;
}
//调用
Screen myScreen;
myScreen.move(4, 0).set('$'); //对吗,为什么
myScreen.display().set('*'); //对吗,为什么

第一个调用对,第二个错,因为第一个返回的是引用,是左值,是它的身份,所以可以连续调用;第二个错是因为display函数返回的是常量引用,无法通过常量引用去改变它的值。 那我们应该怎么办呢?简单的方法是,你把display函数的const去掉就好了啊,但是我们觉得不妥,因为display函数不改变对象内容,设置成const是一个很好的行为,我们的解决办法是重载:

class Screen
{
public:
    Screen &display()
    {
        do_display();
        return *this;
    }
    const Screen &display()
    {
        do_display();
        return *this;
    }
private:
    void do_display() const
    {
        cout << this->contents << endl;
    }
};

我们把打印的任务交给了一个private函数,对外的接口是两个重载函数,试着来调用一下这样行不行:

Screen myScreen(2, 4);
const Screen blank();
myScreen.display().set('*'); //对
blank.display(); //对
blank.display(); //错

就解释到这儿了,欢迎讨论啊,不知道我的理解对不对。

类类型

每个类定义了唯一的类型。对于两个类来说,即使它们的成员完全一样,这两个类也是不同类型,不能互相赋值(我的理解是,类的名字也算类的类型,所以它们不同),我们使用类类型就跟在、使用内置类型一样的定义,这也是C++赋予我们的方便:

int a;
Screen b;
类的声明
class Screen;

向程序中引入名字Screen并且指明它是一种类类型,但是我们不知道它里面有什么。

对于一个类来说,我们必须定义它之后再去创建它的对象,不然编译器不知道给你这个对象分配多少内存。
得到一个结论,一个类的成员类型不能是它自己,但是可以是它的指针或者引用。

友元再探

这一段我就总结了一句话,都可以是朋友。

类之间的友元
class Screen
{
    friend class Window_mgr;    
    //Window_mgr可以访问Screen所有成员
}
令成员函数成为友元

这个之前引出友元概念的时候就介绍了:

class Screen
{
    friend void Window_mgr::clear();    
    //这里是跟人家的成员函数做朋友
}
函数重载和友元

a和b是重载函数,a是Screen的朋友,不代表b也是,一句话,把重载函数看成不同的函数

友元函数和作用域

这个书上的讲法它拗口了,看了我半天,总结如下,必须在类外部声明友元函数之后,类才能去调用它:

struct X
{
    friend void f(){} //友元函数,定义在类内部
    X(){ f(); } //默认构造函数调用f
    //这种调用时错误的,因为f没有被声明

    //俩成员函数的声明
    void g();
    void h();
}

void X::g(){f();} //错误,f没有被声明
void f(); //好,现在声明了
void X::h() {f();} //这样就对了

还有一个问题,我怎么才能在构造函数中调用友元函数呢?我觉得是不行的,欢迎讨论。

#C++工程师#
全部评论
在类的外部之前声明一下,构造函数就可以调用友元函数了呀~
点赞 回复 分享
发布于 2018-10-06 12:38
构造函数可以调用友元函数,构造函数的定义必须在友元函数的声明之后
点赞 回复 分享
发布于 2018-10-07 23:20
抄你点代码,做笔记
点赞 回复 分享
发布于 2019-04-18 15:54
抄你点代码,做笔记
点赞 回复 分享
发布于 2019-09-11 16:06

相关推荐

点赞 评论 收藏
分享
牛客154160166号:9月底还给我发短信,好奇怪,我24届的
点赞 评论 收藏
分享
10-14 23:01
已编辑
中国地质大学(武汉) Java
CUG芝士圈:虽然是网上的项目,但最好还是包装一下,然后现在大部分公司都在忙校招,十月底、十一月初会好找一些。最后,boss才沟通100家,别焦虑,我去年暑假找第一段实习的时候沟通了500➕才有面试,校友加油
点赞 评论 收藏
分享
6 收藏 评论
分享
牛客网
牛客企业服务