在C++中,用类来定义自己的抽象数据类型(abstract data type)。通过定义类型来对应气概解决的问题中的各种概念,可以使我们更容易编写、调试和修改程序。
类的定义和声明
类定义
类成员
最简单地说来,类就是定义了一个新的类型和一个新的作用域。
每个类可以没有成员,也可以定义多个成员,成员可以是数据、函数或类型别名。
所有成员必须在类的内部声明,一旦类定义完成后,就没有任何方式可以增加成员了。
构造函数
构造函数是一个特殊的、与类同名的成员函数,用于给每个数据成员设置适当的初始值。
构造函数初始化列表由成员名和带括号的初始值组成,跟在构造函数的形参表之后,并以冒号开头。
成员函数
在类内部,声明成员函数是必需的,而定义成员函数则是可选的。在类内部定义的函数默认为inline。可以在类定义体内部指定一个成员为inline,作为其声明的一部分。或者,也可以在类定义体外部的函数定义上指定inline。
将关键字const加在形参表之后,就可以将成员函数声明为常量。
数据抽象和封装
类背后蕴涵的基本思想是数据抽象和封装。
数据抽象是一种依赖于接口和实现分离的编程(和设计)技术。
成员函数可以重载。
类声明与类定义
不完全类型只能以有限方式使用。不能定义该类型的对象。不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。
类对象
定义一个类时,也就是定义了一个类型。一旦定义了类,就可以定义该类型的对象。定义对象时,将为其分配存储空间,但(一般而言)定义类型时不进行存储分配。
为什么类的定义以分号结束
类的定义以分号结束。分号是必需的,因为在类定义之后可以接一个对象定义列表。
隐含的this指针
成员函数具有一个附加的隐含形参,即指向该类对象的一个指针。这个隐含武圣命名为this,与调用成员函数的对象绑定在一起。
不能从const成员函数返回指向类武圣的普通引用。const成员函数只能返回*this作为一个const引用。
基于成员函数是否为const,可以重载一个成员函数。
可变数据成员永远都不能为const,甚至当它是const对象的成员时也是。const成员函数可以改变mutable成员。要将数据成员声明为可变的,必须将关键字mutable放在成员声明之前。
构造函数
构造函数的工作是保证每个对象的数据成员具有合适的初始值。
构造函数的名字与类的名字相同,并且不能指定返回类型。
构造函数可以重载,可以为一个类声明的构造函数的数量没有限制,只要每个构造函数的形参表是唯一的。
实参决定使用哪个构造函数。
只要创建该类型的一个对象,编译器就运行一个构造函数。
构造函数不能声明为const。
构造函数初始化式
构造函数初始化列表以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。
构造函数初始化式只在构造函数的定义中而不是声明中指定。
没有默认构造函数的类类型的成员,以及const或引用类型的成员,不管是哪种类型,都必须在构造函数初始化列表中进行初始化。
化const或引用类型成员的唯一机会是在构造函数初始化列表中。
成员被初始化的次序就是定义成员的次序。
默认构造函数
只要定义一个对象时没有提供初始化式,就使用默认构造函数。为所有形参提供默认实参的构造函数也定义了默认构造函数。
一个类哪怕只定义了一个构造函数,编译器也不会再生成默认构造函数。这条规则的根据是,如果一个类在某种情况下需要控制对象初始化,则该类很可能在所有情况下都需要控制。
可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换。
可以通过将构造函数声明为explicit,来防止在需要隐式转换的上下文中使用构造函数。explicit关键字只能用于类内部的构造函数声明上。在类的定义体外部所做的定义上不再重复它。
通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为explicit。将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以地构造对象。
没有禰构造函数并且其全体数据成员均为public的类,可以用与初始化数组元素相同的方式初始化其成员。
友元
友元(friend)机制允许一个类将对其非仅有成员的访问权授予指定的函数或类。友元的声明以字friend开始。它只能出现在类定义的内部。
static类成员
类可以是静态成员,而不是定义一个可普遍访问的全局对象。
static数据成员独立于该类的任意对象而存在;每个static数据成员是与类关联的对象,并不与该类的对象相关联。
在类外部定义static成员时,无须重复指定static保留字,该保留字只出现在类定义体内部的声明处。
static数据成员可以声明为任意类型。
static数据成员必须在类定义体的外部定义,static成员不是通过类构造函数进行初始化,而是应该在定义时进行化。
const static数据成员可以在类的定义体中进行初始化。const static数据成员在类的定义体中初始化时,该数据成员仍必须在类的定义体之外进行定义。