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++工程师#