C++语法复习
Primer C++ review
OOP
继承
虚函数
- 类的三个用户分别是:类的实现者,类的普通用户和类的继承者(派生类):
1 、类的实现者:类的设计者,拥有最大的权限,可以访问类中任何权限的成员,主要负责编写类的成员和友元的代码。可以访问类中的公有部分(public),保护部分(protect)和(private)私有部分。
2、类的普通用户:就是使用类的对象,这部分用户只能访问类的接口(也就是公用部分public)。
3、类的继承者:就是派生类。派生类能访问基类中的公有部分和受保护的部分,普通用户不能访问受保护的成员。 - 基类应该将类型相关的函数与派生类不做改变直接继承的函数区分对待。对于某些函数,基类希望它的派生类各自定义自身的版本,此时基类就将这些函数声明为虚函数。
- 派生类必须在其内部对所有重新定义的虚函数进行声明。派生类可以在这样的函数之前加上
virtual关键字,但也可以不写这个关键字,一旦某个函数被声明成虚函数,则在所有派生类中它是虚函数。 - 在函数声明的分号前面可以使用
override关键字来显式覆盖虚函数,如果这个函数因为参数错误未能覆盖相应虚函数,编译器会报错,这是一个帮助程序员排bug的关键字。 - 只有虚函数才能使用
override关键字,普通成员函数不可以使用,否则会报错。 final关键字同理只能被虚函数使用,并且如果加了这个关键字,后面的派生类就不可以再覆盖这个虚函数了。- 虚函数可以实现动态绑定,但是需要通过引用或者指针调用,否则通过传值调用,类型转换后就不能动态绑定。并且只有继承访问说明符是
public时才可以把派生类的对象绑定到基类对象的引用或者指针上。 - 继承关系中根节点的类通常都会定义一个虚析构函数,即使该函数不执行任何实际操作。
- 派生类可以继承其基类的成员,但是遇到虚函数的时候,派生类应该对其重新定义,即需要提供自己的新定义覆盖(
override)从基类继承来的旧定义。 - 但是也可以不提供自己的定义,从而可以使用基类的定义。
- 派生类覆盖基类的虚函数时,函数签名应该保持一致,但是有一个例外,当虚函数的返回值是基类的引用或者指针时,派生类的覆盖定义可以把这个引用或者指针的类型改为派生类的类型的指针或者引用,但是前提是派生类到基类的转换是可访问的,即是公有继承。
对于纯虚函数:
- 任何构造函数之外的非静态函数,都可以是虚函数,关键字
virtual只能出现在类的内部声明语句之前,不能用于类外部的函数定义,如果基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数。 - 成员函数如果没有被声明成虚函数,则其解析过程发生在编译时而非运行时。
- 虚函数也有默认实参,并且如果某次调用使用了默认实参,该实参值由本次调用的静态类型决定,即如果通过基类的引用调用一个派生类的虚函数,此时使用的还是基类的默认实参。
- 有时我们需要回避虚函数,即强行调用某个类的某个特定虚函数,此时需要使用作用域描述符调用: baseP->base::function(); 一般需要回避虚函数的场景是:通过派生类的虚函数调用基类的虚函数,基类的虚函数此时进行一些所有类都需要的工作,派生类再做一些额外的工作。
- 纯虚函数:纯虚函数的目的是在于提出一个所有类都应该有的接口,但是基类本身并没有这个接口的具体实现,必须由派生类来实现这个接口。
- 纯虚函数的声明方式是在分号前加一个
=0。 - 含有纯虚函数的类叫做抽象基类,抽象基类不能实例化,只能有引用和指针,此时他们指向的实际上是它的派生类的对象。
- 派生类可以把基类中的虚函数定义为纯虚函数(吗?)
- 基类的析造函数应该是虚的:当一个基类的指针指向一个派生类时,如果析构函数不是虚的,此时调用析构函数会调用基类的,大多数情况下这不是所期望的。
- 构造函数绝对不能是虚函数,这是不被支持的错误语法。因为
A virtual call is a mechanism to get work done given partial information. In particular, "virtual" allows us to call a function knowing only an interfaces and not the exact type of the object. To create an object you need complete information. In particular, you need to know the exact type of what you want to create. Consequently, a "call to a constructor" cannot be virtual.
类型转换
- 不管是公有继承还是私有继承,派生类中都有基类中的全部成员。
- 公有继承的派生类可以在使用基类指针或者基类引用的地方使用。
- 基类不能隐式向派生类转换。但是可以通过派生类的构造函数隐式转换,此时和普通的转换一致。
- 并且需要注意的是,即使存在一个基类的指针指向派生类,也不能把这个指针赋值给派生类的一个指针。因为编译器不能在编译时确定基类指针的动态类型。
- 但是如果能够确定某个基类向派生类的转换是安全的,则可以使用
static_cast来强制覆盖编译器的检查工作。 - 如果基类中有一个或者多个虚函数,可以使用
dynamic_cast请求一个类型转换,该转换的安全检查在运行时执行。????????
派生类的构造函数
- 派生类的默认构造函数会调用基类的默认构造函数。
- 派生类可以自定义构造函数,在构造函数的冒号后面调用基类的相应构造函数,需要给基类的构造函数传相应参数,此时会首先初始化基类,再按照声明顺序构造派生类的成员,这与构造函数的初始化顺序无关。
- 虽然可以在派生类中初始化基类的公有成员和受保护成员,但是这是不好的行为,应该使用基类的接口:构造函数,初始化基类。
- 派生类只能调用直接基类的构造函数,不能调用间接基类的构造函数,构造过程通过深度优先构造。
防止继承的发生
- 在类名后面加一个
final:class A final{};class B final: public A{};
静态类型和动态类型
- 静态类型就是对象的声明类型;动态类型就是对象所指向的内存所代表的实际类型。
- 只有指针和引用的动态类型和静态类型会不同,其他的对象的两个类型是一致的。
访问控制
- 派生类可以继承定义在基类中的成员,但是不一定可以访问它们。派生类可以访问基类的公有成员,不能访问私有成员,
protected成员是为了让派生类可以访问,而派生类的用户和其他用户不能访问。 - 继承访问说明符的作用是控制派生类或者派生类的用户访问基类成员的访问权限:公有继承的派生类,它的用户可以按照通常的方式访问基类的成员;私有继承的派生类,它的用户不能访问基类的全部成员,即对于派生类的用户来说,基类全部成员是
private的;而受保护的派生类,基类的公有成员对于它来说是受保护的。 - 继承访问说明符对派生类向基类的转换也有影响
- 对于普通用户代码来说,只有公有继承才可使用此转换,私有和受保护的继承不可转换。
- 对于派生类的成员和友元来说,三个类型的继承都可以使用类型转换。
- 对于派生类的派生类来说,它的友元和成员函数可以使用公有的继承和受保护的继承转换,但是不可以转换私有的。
友元与继承
- 友元不能传递,也不能继承: 基类的友元访问派生类新的成员没有特殊性,但是可以访问基类的部分,即基类的友元不是派生类的友元,但是此友元可以访问派生类中对应的基类部分。派生类没有继承基类的友元。而友元的派生类仍然是友元,但是新定义的函数以及重写的虚函数不是友元。

浙公网安备 33010602011771号