成员函数的声明必须在类内部,定义可内可外,内部的是隐式内联的。
当调用一个成员函数的时候,this指针被调用对象初始化。
C++允许把this指针声明成指向const对象的指针,在成员函数参数列表后面加const即可,const对象只能调用这种成员函数。
类本身是一个作用域,成员函数嵌套在类作用域内部。
编译器分两步处理类:首先编译成员的声明(包括成员函数的声明),然后才是成员函数体。因此成员函数体可以使用在该函数声明之后才声明的成员。
类外部定义成员函数的时候函数的定义必须与类内部的声明一致,而且函数名还要加上类名::,从::开始的内容就属于类域了,所以函数的参数和函数体可以直接使用类成员。
IO类不能被拷贝赋值,读写操作会改变其内容,一般使用非常量引用进行传递。
拷贝类的对象其实拷贝的是类的数据成员。
构造函数名字与类名相同而且没有返回类型。如果构造的对象是const的,那在构造函数内部也是可以改变对象的值的,因为构造函数完成初始化之后对象才算获得常量属性。构造的过程可以认为是对对象的一种改变,所以构造函数不能被声明为const的。
没有定义任何构造函数的时候编译器会自动合成一个默认的构造函数,这个构造函数对数据成员进行默认初始化。如果数据成员有初始化值,就用它来初始化。
一旦程序员定义了构造函数,那编译器将不再合成默认构造函数,如果还有默认构造函数的需求,那程序员就得自己定义默认构造函数。
C++11标准当中,需要使用默认行为可以把函数的定义部分,就是{...}替换成=default.
构造函数可以通过构造函数初始值列表对数据成员指定初始化的值,尤其是数据成员没有默认构造函数而且没有在定义时指定初值时特别有用。初始值列表的顺序不影响数据成员初始化的顺序,只影响初始化的值。
一般编译器合成的默认拷贝控制函数将对数据成员依次进行对应的拷贝、赋值、析构操作。
private说明符之后的成员只能被类自己的代码访问(别的代码需要的话可以声明友元)。
class和struct关键字作用上唯一的区别就是第一个访问说明符之前的成员访问权限不同,前者是private,后者是public.
类可以通过友元声明让别的类、函数、类的成员函数访问自己的非公有成员。友元声明的方式是在类内写一条friend开头的声明就行。友元声明只说明了访问权限,并不说明该标识符可以被使用。
所以全局函数就算在类内部声明了友元也还得在外面有单独的声明。友元声明在类的内部的位置在哪作用都是一样的,不受到访问说明符的限制。
如果在类的内部定义了类型的成员(例如使用using=XXX),那这个成员必须先声明后使用。
如果需要把成员函数声明为inline,最好只在定义的时候说明(虽然声明的时候也可以加inline),提高可读性。
成员可以重载,规矩和普通函数一样。
通过在数据成员的声明前面加入mutable,可以使得const的成员函数也能修改这个数据成员。
通过区分成员函数是不是const,可以进行重载。非const对象精确匹配非const版本,而const对象只能匹配const版本。
就算两个类的数据成员一模一样,这两个类也是不同的类型。
C++同样支持C语言当中class/struct Type 标识符 的定义方式。
可以只声明类而不定义它,在定义它之前只能声明它的指针和引用。
友元函数是可以定义在类的内部的,这种定义是隐式inline的,但是仍然需要再外部提供单独的函数声明。
友元关系不具有传递性。
在类的外部,通过类名::来访问类级别的成员。
在类的成员声明当中使用的名字都必须在前面有过声明(确保可见)。因为编译器编译声明的时候是按照声明的顺序来的。
一般来说,内层作用域可以定义外层作用域的名字,但是这里有个例外,如果这个名字是类型那就不行。
对于成员函数当中使用的一个名字,查找过程如下:首先在函数内部找,找不到就去类内部找,再找不到就去成员函数定义之前的作用域去找(如果成员函数定义在类外的话),
再找不到就报错。
通过委托构造函数,可以先让另一个构造函数进行初始化,然后再继续该构造函数的初始化。
默认初始化的情况:在块作用域内不使用任何一个初始值定义一个非静态变量或者数组时;当一个类本身含有类类型的成员且使用合成的默认构造函数时(程序员未定义任何构造函数时);
当类类型的成员没有在构造函数初始值列表中显式地初始化时。
值初始化的情况:
在数组初始化的过程中如果我们提供的初始值数量少于数组的大小,剩下的部分采用值初始化;
当我们不使用初始值定义一个局部静态变量时;
当我们通过书写 T() 的表达式显式请求值初始化时。
这两种情况类都必须含有一个默认构造函数。
当我们定义一个只有一个参数的构造函数的时候,实际上是定义了这种参数类型到该类的一个转换。比如A类定义了一个只有一个参数的构造函数,这个参数类型为int.那么实际上这也定义了一种int类型到A类的转换,任何使用A类的地方都可以直接赋一个int,这个int会被构造成一个A类。
可以在构造函数前面加上explicit关键字抑制这种隐式的转换。
聚合类有特殊的初始化语法。需要的时候再细看。还有字面值常量类,一堆constexpr。
通过在成员前面加上static声明静态成员,这些成员属于类不属于对象,所以没有this指针。
在类外通过 类名::成员 访问。
静态成员的声明必须在类内,静态函数的定义可内可外,当在外部定义的时候不能重复static关键字。
对于静态的数据成员,除非是const或者constexpr而且具有字面值初始值的,否则都应该在类的内部声明,在类的外部定义和初始化。
静态成员存在于程序运行期间,在第一次被使用时初始化。