类定义了数据成员和函数成员:数据成员用于存储与该类类型的对象相关联的状态;而函数成员则负责执行赋予数据意义的操作。
第12章 类
一个类可以包含若干公有的、私有的和受保护的部分:在public部分定义的成员可被使用该类型的所有代码访问;在private部分定义的成员可被其他类成员访问。
所有成员必须在类的内部声明。
const成员函数不能改变其所操作的对象的数据成员。const必须同时出现在声明和定义中,若只出现其中一处,就会出现一个编译时错误。
类背后蕴涵的基本思想是数据抽象和封装。
数据抽象是一种依赖于接口和实现分离的编程(和设计)技术。
封装是一项将底层次的元素组合起来形成新的、高层次实体的技术。
可以在任意的访问标号出现之前定义类成员。在类的左花括号之后、第一个访问标号之前定义成员的访问级别,其值依赖于类是如何定义的。如果类是用struct关键字定义的,则在第一个访问标号之前的成员是公有的;如果类是用class关键字定义的,则这些成员是私有的。
如果在多个文件中定义一个类,那么每个文件中的定义必须是完全相同的。
将类定义放在头文件中,可以保证在每个使用类的文件中以同样的方式定义类。
不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。
类的前向声明一般用来编写相互依赖的类。
类的定义以分号结束。分号是必需的,因为在类定义之后可以接一个对象定义列表。例:
class Sales_item {/*...*/};
class Sales_item {/*...*/} accum,trans;
如果返回类型使用由类定义的类型,则必须使用完全限定名。例如:
class Screen
{
public:
typedef std::string::size_type index;
index get_cursor() const;
};
inline Screen::index Screen::get_cursor() const
{
return cursor;
}
构造函数是特殊的成员函数,只要创建类类型的新对象,都要执行构造函数。构造函数的工作是保证每个对象的数据成员具有合适的初始值。
构造函数的名字与类的名字相同,并且不能指定返回类型。
构造函数不能声明为const,例:
class Sales_item
{
public:
Sales_item() const; //error
}
构造函数初始化式只在构造函数的定义中而不是声明中指定。
没有默认构造函数的类类型的成员,以及const或引用类型的成员,不管是哪种类型,都必须在构造函数初始化列表中进行初始化。例:
class ConstRef
{
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
// no explicit constructor initializer:error ri is uninitialized
ConstRef::ConstRef(int ii)
{ // assignment:
i=ii; // ok
ci=ii; //error:cannot assign to a const
ri=ii; //assigns to ri which was not bound to an object
}
记住,可以初始化const对象或引用类型的对象,但不能对它们赋值。在开始执行构造函数的函数体之前,要完成初始化。初始化const或引用类型数据成员的唯一机会是在构造函数初始化列表中。编写该构造函数的正确方式为:
//ok:explicitly initialize reference and const members
ConstRef::ConstRef(int ii): i(ii), ci(ii), ri(ii) {}
成员被初始化的次序就是定义成员的次序。
如果一个成员是根据其他成员而初始化,则成员初始化的次序是至关重要的。
内置和复合类型的成员,如指针和数组,只对定义在全局作用域中的对象才初始化。当对象定义在局部作用域中时,内置或复合类型的成员不进行初始化。
如果类包含内置或复合类型的成员,则该类不应该依赖于合成的默认构造函数。它应该定义自己的构造函数来初始化这些成员。
可以通过将构造函数声明为explicit ,来防止在需要隐式转换的上下文中使用构造函数。
explicit 关键字只能用于类内部的构造函数声明上。
友元(friend)机制允许一个类将其非公有成员的访问权授予指定的函数或类。友元的声明以关键字friend开始。它只能出现在类定义的内部。
每个static数据成员是与类关联的对象,并不与该类的对象相关联。
static成员是类的组成部分但不是任何对象的组成部分,因此,static成员函数没有this指针。
static数据成员必须在类定义体的外部定义(正好一次)。不像普通数据成员,static成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。
static关键字只能用于类定义体内部的声明中,定义不能标示为static。
小结:
类是C++中最基本的特征,允许定义新的类型以适应应用程序的需要,同时是程序更短且更易于修改。
数据抽象是指定义数据和函数成员的能力,而封装是指从常规访问中保护类成员的能力,它们都是类的基础。成员函数定义类的接口。通过将类的实现所用到的数据和函数设置为private来封装类。
类可以定义构造函数,它们是特殊的成员函数,控制如何初始化类的对象。可以重载构造函数。每个构造函数应初始化每个数据成员。初始化列表包含的是名-值对,其中的名是一个成员,而值则是该成员的初始值。
类可以将其非public成员的访问权授予其他类或函数,并通过将其他的类或函数设为友元来授予其访问权。
类也可以定义mutable或static成员。mutable成员永远都不能为const;它的值可以在const成员函数中修改。static成员可以是函数或数据,独立于类类型的对象而存在。
第13章 复制控制
第14章 重载操作符与转换
IO操作符必须为非成员函数。 类通常将IO操作符设为友元。
设计输入操作符时,如果可能,要确定错误恢复措施,这很重要。
一般而言,将算数和关系操作符定义为非成员函数。
既定义了算数操作符又定义了相关复合赋值操作符的类,一般应使用复合赋值实现算数操作符。
类定义下标操作符时,一般需要定义两个版本:一个为非const成员并返回引用,另一个为const成员并返回const引用。
转换操作符是一种特殊的类成员函数。它定义将类类型值转变为其他类型值的转换。转换操作符在类定义体内声明,在保留字operator之后跟着转换的目标类型,通用形式如下:
operator type();
这里,type表示内置类型名、类类型名或由类型别名所定义的名字。对任何可作为函数返回类型的类型(除了void之外)都可以定义转换函数。一般而言,不允许转换为数组或函数类型,转换为指针类型(数据和函数指针)以及引用类型是可以的。
转换函数必须是成员函数,不能指定返回类型,并且形参表必须为空。
语言只允许一次类类型转换。