十九.类
1、构造函数
构造函数的参数初始化表:Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){};
初始化 const 成员变量的唯一方法就是使用参数初始化表;
构造函数参数初始化顺序与初始化表列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关;
构造函数可以被重载,一个类最少有一个构造函数,析构函数不能被重载,一个类只能有一个析构函数;
2、this指针
this指针只能用在类的内部,通过 this 可以访问类的所有成员,包括private、protected、public属性的。
this 是 const 指针,它的值是不能被修改的,一切企图修改该指针的操作,如赋值、递增、递减等都是不允许的。
this 只能在成员函数内部使用,用在其他地方没有意义,也是非法的。
只有当对象被创建后 this 才有意义,因此不能在 static 成员函数中使用(后续会讲到 static 成员)。
this 作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。
3、static静态成员变量
1) 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。
2) static成员变量和普通static变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。
3) 静态成员变量必须初始化,而且只能在类体外进行。例如:int Student::m_total = 10;初始化时可以赋初值,也可以不赋值。如果不赋值,那么会被默认初始化为 0。全局数据区的变量都有默认的初始值 0,而动态数据区(堆区、栈区)变量的默认值是不确定的,一般认为是垃圾值。
4) 静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。当通过对象名访问时,对于不同的对象,访问的是同一份内存。
4、static静态成员函数
静态成员函数与普通成员函数的根本区别在于:普通成员函数有this指针,可以访问类中的任意成员;而静态成员函数没有this指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
5、friend友元函数
在当前类以外定义的、不属于当前类的函数也可以在类中声明,但要在前面加 friend 关键字,这样就构成了友元函数。友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。
友元函数可以访问当前类中的所有成员,包括 public、protected、private 属性的
友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象,友元关系不能被继承;
如将类 B 声明为类 A 的友元类,那么类 B 中的所有成员函数都是类 A 的友元函数,可以访问类 A 的所有成员,包括 public、protected、private 属性的
友元的关系是单向的而不是双向的。如果声明了类 B 是类 A 的友元类,不等于类 A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。
友元的关系不能传递。如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类。
6、C++ class和struct的区别
在C语言中,struct 只能包含成员变量,不能包含成员函数,而在C++中,struct 类似于 class,既可以包含成员变量,又可以包含成员函数。
C++中的 struct 和 class 基本是通用的,唯有几个细节不同:
1)使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。
2)class 继承默认是 private 继承,而 struct 继承默认是 public 继承。
3)class 可以使用模板,而 struct 不能。
7、类的继承
类的构造函数不能被继承,解决方法:在派生类的构造函数中调用基类的构造函数。
构造函数的调用顺序是按照继承的层次自顶向下、从基类再到派生类的,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的(那样间接基类的构造函数就会被调用多次,造成浪费)。
派生类创建对象时必须要调用基类的构造函数,定义派生类构造函数时最好指明基类构造函数;如果不指明,就调用基类的默认构造函数;如果没有默认构造函数,那么编译失败。\
创建派生类对象时,构造函数的执行顺序和继承顺序相同,即先执行基类构造函数,再执行派生类构造函数。
而销毁派生类对象时,析构函数的执行顺序和继承顺序相反,即先执行派生类析构函数,再执行基类析构函数