1. 类
允许程序员定义自己的类型,它们用起来与内置类型一样容易和直观
简单来说,类就是定义了一个新的类型和一个新的作用域
每个类可以没有成员,也可以定义多个成员,成员可以是数据,函数或类型别名
构造函数一般使用一个构造函数初始化列表,来初始化对象的数据成员
将const 关键字加在形参表后,可以将成员函数声明为常量
在类的左花括号起,第一个访问标号之前定义成员的访问级别是默认级别,即如果使用struct 定义,默认为public ,如果使用class定义,则默认为private
在类内部定义的函数为inline 函数,在任何被调用的地方直接展开该函数;也可以在声明是通过inline 关键词显示指定成员函数为inline 函数;
可以声明一个类而不定义它,此时它是一个不完全类型(已知是个类型,但不知道具体保护哪些成员)。不完全类型只能用于定义指向该类型的指针和引用, 而不能定义具体的对象,因为编译器不能给不完全类型分配空间。
因为只有当类定义体完后才能定义类,所以类不能具有自身类型的数据成员,而只能有指向自身类型的引用或指针
成员函数有一个附加的隐含形参this指针, 与调用成员函数的对象绑定在一起。 成员函数不能显示定义this ,但是可以显示使用this 指针。
显示使用this 指针最常见的情况是:该函数返回对调用该函数对象的引用
普通函数可以基于指针形参是否指向一个const 对象进行重载,在类定义中,可以基于成员函数是否为const 进行重载。
可以使用mutable 关键词指定可变成员,可变成员永远可以修改,甚至是当它为const 对象的成员。const 成员函数也可以改变mutable 成员
每个类都定义了自己的作用域,即使两个类具有相同的成员列表,它们也是不同的类型,有不同的作用域;
类的定义体以及成员函数的形参表,函数体都处于类的作用域中,函数的返回类型不一定在类的作用域中;
类的作用域中可以直接使用类的成员(编译器隐式进行this 指针的操作), 在类的作用域外可以通过对象的. 或者指针的->操作符来访问类的成员;
类型需要在使用前进行定义, 类的作用域中的类型也一样;
类型查找的一般原则是从当前作用域开始查找,没有找到则查找包含当前作用域的作用域,一直查找到全局作用域。 作用域查找只查找使用该类型之前的部分,所以类型需要先定义再使用;
类型查找的原则决定了当局部变量和全局变量同名时,一般局部变量会屏蔽全局变量,因为局部变量的类型定义更先查找到。
当类的成员函数形参变量与成员变量同名时,在成员函数的函数体中,形参变量会屏蔽成员变量,这时可以使用this 指针来显示访问成员变量;
当类作用域中的变量与全局变量同名时,在类作用域中全局变量会被屏蔽,这时可以使用全局作用域(::)来显式指定使用全局变量
构造函数是特殊的成员函数,只要创建类类型的新对象,都要执行构造函数。构造函数的工作是保证每个对象的数据成员都有合适的初始值;
构造函数的名字与类的名字相同,且不能指定返回类型。 和其他函数一样,可以有0个或者多个形参,可以进行重载;
只要创建该类型的一个对象编译器就允许一个构造函数;
构造函数不能声明为const, 要创建该类类型的const 对象,可以使用一个普通的构造函数来初始化该const 对象;
构造函数与其他函数不同的是,构造函数可以包含一个构造函数初始化列表。构造函数初始化列表以一个冒号开始,接着的是一逗号分隔的数据成员列表,每个数据成员后面跟一个放在园括号中的初始化式。 构造函数初始化式只能在构造函数的定义中而不是声明中指定。
从概念上讲,可以认为构造函数分为两个阶段执行:1 初始化阶段 2普通的计算阶段 ,计算阶段由构造函数的函数体中的所有语句组成。
在构造函数初始化列表中初始化成员和在构造函数体中对数据成员赋值,最终的结果相同。 但是在初始化列表中进行初始,构造函数只执行可初始化阶段。 在构造函数体中对数据成员赋值, 实际的过程是先运行数据成员的默认构造函数初始化成员, 再对已经有值得成员进行赋值。
对于没有在构造函数初始化列表中显示提及的成员,使用与初始化变量相同的规则进行初始化:类类型数据成员通过该类型的默认构造函数初始化, 内置或者复合类型(指针,数组)若定义在全局作用域中则被初始化为0,若定义在局部作用域中则不被初始化。
有些成员在构造函数体中对它们进行赋值不起作用,这种情况下必须使用构造函数初始化列表, 这些类型包括: 没有默认构造函数的类类成员,const 或者引用类型的成员;
必须对const 或者引用类型以及没有默认构造函数的类类型的的任何成员使用初始化式
数据成员初始化的顺序就是其在类中定义的顺序,第一个定义的成员先进行初始化,和在构造函数初始化列表中写的顺序无关;
初始化的顺序通常无关紧要,但如果一个成员根据其他成员而初始化,则顺序至关重要。 最佳的实践是按照与成员声明一直的次序编写构造函数初始化列表;
我们更喜欢使用默认实参,因为它可以减少代码重复;
当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。
当为一个类定义了非默认构造函数时,该类类型就成为了没有默认构造函数的类型,因为只要定义了一个构造函数,编译器就不会再生成默认构造函数;
编译器合成的默认构造函数使用与变量初始化相同的规则来初始化成员
最佳实践:如果定义了其他构造函数,则提供一个默认构造函数几乎总是对的
默认构造函数的使用方式:
对于class A , A a 或者 A a = A()
两种方式是等价的,第一种方式使得类类型的使用和内置类型一样,第二中方式显示调用默认构造函数
可以用单个实参调用的构造函数定义了从形参类型到该类类型的一个隐式类型转换
最佳实践:
通常除非有非常明显的理由要定义隐式类型转换,否则单形参构造函数都应使用explicit 关键词抑制隐式转换。 并且当转换有用时,用户可以显式的构造对象;
在某些情况下,允许特定的非成员函数访问一个类的是有成员,同时仍然阻止一般的访问,是非常方便的。
友元(friend)机制允许一个类将其非公有成员的访问权授予指定的函数或者类。
友元声明以关键词friend 开始,友元声明只能出现在内定义的内部;通常将友元声明成组的放在类定义的开始或者结尾
友元可以是普通的非成员函数,也可以是前面已定义的类的成员函数,或者类
对于特定类类型的全体对象而言,访问一个全局对象有时是必要的。 static 成员独立于类类型的任意对象存在,static 成员是与类关联的对象,而不是与该类的对象关联
static 成员可以是成员数据或者成员函数,static 函数没有this 指针