类
类的其他特性
类成员再探
定义一个类型成员
除了定义数据和函数成员之外,类还可以自定义某种类型在类中的别名。由类定义的类型名字和其他成员一样存在访问限制,可以是public或者private中的一种,如:
public:
typedef std::string::size_type pos;
与此等价,也可使用类型别名:
public::
using pos = std::string::size_type pos;
需要注意的是,用来定义类型别名的成员必须先定义后使用。
类的构造函数
如果提供了构造函数,那么编译器将不会自动生成默认的构造函数。如果需要,必须显式地声明默认构造函数,代码如下:
MyClass() = default; //=default告诉编译器自动合成默认的构造函数
令成员作为内联函数
定义在类内部的成员函数是自动inline的,也可在类外部定义的地方说明inline,这样可以使类更容易理解。
和需要在头文件定义inline函数的原因一样(内联函数在程序中展开时将多次定义,为保持多个定义完全一致,内联函数通常定义在头文件中),inline成员函数也应该与相应的类定义在同一个头文件中。
重载成员函数
和非成员函数一样,成员函数也可被重载,只要函数之间在参数的数量和/或类型上有区别就行。
可变数据成员
一个可变数据成员(mutable data member)永远不会是const,即使它是const对象的成员。因此,一个const成员函数可以改变一个可变成员的值。声明方法如下:
mutable size_t num_ctr;
类数据成员的初始值
类数据成员初始化最好的方式就是将数据默认值声明成一个类内初始值。当我们提供一个类内初始值时,必须使用①. 符号=的初始化形式。②. 花括号括起来的直接初始化形式。如:
private:
int my_num_ = 0;
或者
private:
int my_num_ {0};
含有指针数据成员的类一般不宜使用默认的拷贝和赋值操作,如果类的数据成员都是内置类型,则不受干扰。
返回*this的成员函数
返回引用的函数是左值的,意味着这些函数返回的是对象本身而非对象的副本。例:
Screen& Screen::set(pos r, pos col, char ch)
{
...
return *this
}
可把一系列这样的操作连接在一条表达式中,这些操作将在同一个对象上执行。例:
myScreen.move(4,0).set(‘#’);
假如定义的返回类型不是引用,则返回值将是*this拷贝产生的副本。
从const成员返回*this
若调用一个const成员函数,则此时this将是一个指向const的指针而*this是const对象。返回类型将是一个const的引用,则不能将此成员函数嵌入到一组动作的序列中去。
基于const的重载
通过区分成员函数是否是const的,我们可以对其进行重载。上述问题的解决方案是,对同一个成员函数定义两个版本:①、在常量对象上调用的const成员函数。②、在非常量对象上调用的非常量版本。
因为这两个版本的成员函数完成相同的功能,所以可以定义一个私有const成员函数,被这两个版本的成员函数调用。其中被非常量版本调用时,this指针将隐式地从指向非常量的指针转换成指向常量的指针。非常量版本函数返回时将返回非常量引用。例:
class Screen {
public:
Screen &display(std::ostream &os) {do_display(os); return *this;}
const Screen &display(std::ostream &os) const {do_display(os); return *this;}
//在函数声明的参数表后面出现的const,指明此函数不会修改此类的状态
private:
void do_display(std::ostream &os) const {os << contents;}
};
建议:对于类内公共代码使用私有功能函数,设计良好的C++代码常常包含大量类似于do_display的小函数。
类类型
可把类名作为类型的名字使用,从而直接指向类类型。也可把类名跟在关键字class或struct后面,例:
Sales_data item1;
class Sales_data item1; //等价的声明
类的声明
可以先声明而暂时不定义类:
class Screen;
这种声明有时被称作前向声明(forward declaration),未完成此类的定义前是一个不完全类型,只可以定义指向这种类型的指针或引用,也可声明(但是不能定义)以不完全类型作为参数或者返回类型的函数。
一个类的成员类型不能是该类自己,但允许包含指向它自身类型的引用或指针。
友元再探
类可以指定友元函数,还可以指定友元类,例:
class Screen{
friends class Window_mgr;
...
}
如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。需要注意的是友元关系不存在传递性,即每个类负责控制自己的友元类或友元函数。
令成员函数作为友元
只把一个成员函数声明成友元时,必须明确指出该成员函数属于哪个类。例:
class Screen{
friend void Window_mgr::clear(ScreenIndex); // Window_mgr::clear必须在Screen类之前被声明
}
函数重载和友元
尽管重载函数的名字相同,但他们仍然是不同的函数。因此,如果一个类想把一组重载函数声明成它的友元,它需要对这组函数中的每一个分别声明。
友元声明和作用域
友元声明的作用仅仅是影响访问权限,它并非普通意义上的声明。换句话说,用声明过友元的类的成员调用该友元函数,它(已被声明为友元的函数)也必须是被声明过的。