一、类的定义
类是一种复杂类型,将不同的类型的数据和相关的操作封装在一起。类是对象的抽象,对象是类的一种实例。
类定义方式
将类的声明和定义放在类内。优点是编译器可以将其识别为内联函数,缺点是会出现头文件包含的问题。
将类的声明和定义分开,声明在类内,定义在类外,以便于重复利用。
二、访问限定符
类的访问限定符分为public、protected、private。
说明:
1.pubic成员可以在类外访问,protected和private成员在类外不可以访问。
2.作用域是从该访问限定符开始到下一个访问限定符出现为止。
3.class默认的访问权限是private,struct默认的访问权限是public。
三、类的作用域
C++中作用域分为:全局域、局部域、类域、名字空间域。
类成员的三种访问形式
1.直接成员运算符(.)
对象.成员变量/成员函数
2.间接成员运算符(->)
对象指针->成员变量/成员函数
3.作用域解析符(::)
常用于类的内部。
四、类的实例化
定义一个类并没有分配实际的内存空间来存储它。只有在实例化时才分配空间。
类实例化的方式
A a;
A* a1 = new A();
两者的区别:前者是在栈中分配内存,后者在堆中分配内存,一般没什么区别,但是动态分配会使对象的可控性增强。大程序用new,小程序直接申请。new最后必须delete,直接申请的空间系统会自动回收存储。
五、类的对象模型
指的是类对象各个成员变量的分布形式,类的大小由非静态成员变量相加,并考虑内存对齐。
六、this指针
this指针是类类型的引用,且被const修饰。它不是对象的一部分,不影响sizeof的结果。this指针是在成员函数调用前创建,在成员函数结束后消失。
this指针是类成员变量的第一个默认隐含参数,编译器自动维护,只有在类的非静态成员中才能使用this指针,其他函数中不能使用。
thiscall调用约定:参数是从右向左压栈的,如果参数确定,this指针通过ecx传递给调用者,如果不确定,this指针在所有参数压栈后被压入栈。对于参数不确定的,调用者清理堆栈,否则函数自己清理堆栈。
七、类的六大默认成员函数
构造函数
1.概念
名字与类名相同,创建类对象时,由编译器自动调用。在对象的生命周期只调用一次,以保证每个数据成员都有一个合适的初始值。
2.特性
* 构造函数的函数名与类名相同;
* 没有返回值;
* 有初始化列表,也可以不使用;
* 若没有显示定义,编译器会自动调用默认的构造函数;
* 无参构造函数和缺省的构造函数都属于缺省构造函数,缺省构造函数只能有一个;
* 不能用const修饰。
3.作用
* 构造对象
* 初始化对象
* 类型转换
4.调用时机
在对象被创建时调用。
5.两个阶段
(1)初始化
初始化可以在初始化列表中进行初始化,也可以在构造函数体内进行初始化。
一般情况下,初始化列表顺序是按照数据成员在类中定义顺序初始化的,如果初始化依赖于其他成员的值,要注意初始化顺序,尽量保持定义顺序初始化。
必须在初始化列表中初始化的场景:引用数据成员、const数据成员、类类型成员。
(2)普通计算阶段(赋值)
给类中数据成员进行重新赋值,会覆盖初始化成员的值。
(3)注意
* 对于普通成员,其值的设定可以放在初始化阶段或赋值阶段;
* 对于const类型的数据成员和引用数据成员,其初始化必须在初始化阶段完成;
* 引用类型变量初始化时必须使用相同的类型的变量进行初始化,不可以使用常量进行初始化。
6.默认的构造函数
如果没有显式定义构造函数,编译器会自动合成一个默认的构造函数,什么都不做。如果显式定义了,编译器不会合成默认的构造函数,最好是给一个无参的构造函数。
7.explict
用explict修饰的构造函数,不允许构造函数隐式转换。
拷贝构造函数
1.概念
只有单个形参,且是类类型的引用,一般用const修饰,编译器自动调用。
2.特性
* 与构造函数构成重载;
* 参数是同类型对象的引用;
* 若没有显示定义,系统会合成一个默认的。
3.作用
用已存在的同类对象进行初始化。
析构函数
1.概念
在对象被销毁时,由编译器自动调用,完成类的一些资源清理工作。
2.特性
* 在类名前加~;
* 没有参数和返回值;
* 一个类中只有一个析构函数,若无定义,系统会自动生成缺省的构造函数;
* 析构函数不是删除对象,而是做一些收尾工作。
3.作用
销毁对象时,做一些资源清理工作。
4.调用时机
在对象被销毁时,做一些清理资源的工作。
赋值运算符重载
常见的重载运算符:++、–、[]、<<
不能重载的运算符:.、.*、::、?:、sizeof
取地址运算符重载
const修饰的取地址运算符重载
八、static成员
1.概念
声明为static的类成员称为类的静态成员。分为静态成员数据和静态成员函数。
2.特性
* 静态成员被所有类对象共享,不属于某个具体的实例;
* 访问方式:类名.静态成员、对象.静态成员;
* 静态成员在对象创建前就创建了;
* 静态成员必须在类外定义,声明为static,定义时不加static关键字;
* 类的静态成员函数没有this指针、super关键字,在类的静态成员函数中不能使用非静态成员变量;
* 静态成员函数也有三种访问权限,返回值,const修饰符;
* 静态成员函数只能访问静态成员,非静态成员函数可以访问静态成员,也可以访问非静态成员。
3.静态成员与普通成员的区别
(1)生命周期
普通成员生命周期随对象,静态成员生命周期随类。
(2)调用方式
普通成员只能被对象调用,静态成员可以被对象调用,也可以被类名调用。
(3)别名
普通成员也称实例变量,静态成员也称类变量。
(4)存储位置
普通成员数据存储在内存的对象中,是对象的特有数据;
静态成员数据存储在方法区的静态区中,共享数据。
九、const修饰的成员
1.概念
const修饰的成员是一个常量,具有宏常量的特性,可以当做真正的常量。
2.使用场景
修饰形参、返回值、成员变量、成员函数等。
修饰的成员变量必须在构造函数的初始化列表中进行初始化,修饰成员函数,其实是修饰成员函数中隐含的this指针,表示在类中不可以对类的任何成员进行修改,如果要对类的某个数据成员进行修改,该数据成员声明和定义必须被mutable修饰。
3.调用规则
* const对象只能访问const成员函数;
* 非const对象可以访问const成员函数,也可以访问非const成员函数;
* const成员函数只能调用其他的const成员函数;
* 非const成员函数可以调用其他const成员函数,也可以调用其他非const成员函数。
十、内联函数
1.概念
用inline修饰的函数是内联函数。编译时,编译器在调用内联函数的地方展开,没有参数压栈,提升程序运行效率。
2.特性
* 声明和定义必须放在一起;
* 以空间换时间,省去函数压栈开销;
* inline对编译器来说只是建议,如果函数体内有递归或循环,编译器优化时会忽略内联;
* 在类内定义的成员函数默认是内联函数,即使不加inline,也是内联函数。
3.内联函数和宏的区别
* 内联函数是函数,宏不是函数;
* 内联函数在编译器时展开,内联函数在预处理展开;
* 内联函数是嵌入目标代码中,宏只是进行简单的文本替换;
* 内联函数有参数类型检测、语句是否正确的检查,提高了程序的安全性;
* 内联函数不会出现二义性,但是宏处理时,最好用括号括起来,避免二义性。
十一、友元
1.友元函数
友元函数定义在类外,在类内部声明上加上friend关键字。友元函数不属于任何类,可以在类定义的任何地方声明,不受类访问限定符的限制。
2.友元类
友元类的所有成员函数可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
3.友元优缺点
优点:提高了程序运行效率。
缺点:破坏了类的封装性和隐藏性。
4.友元特性
友元是单向的,不具有交换性。关系不能传递。