[读书笔记] C++Primer (第5版) 第15章 面向对象程序设计

面向对象程序设计(OOP object-oriented programming)的核心思想:数据抽象、继承和动态绑定。
多态性。

1.继承:

虚函数:几类希望其派生类进行覆盖(override)的函数。如果没有覆盖,派生类会直接继承其在基类中的版本。
派生类可以在这样的函数之前加上virtual关键字,但是不是非得这么做。
基类通常都应该定义一个虚析构函数。即使该函数不执行任何实际操作。
使用指针或引用调用虚函数时,该调用将被动态绑定(也叫运行时绑定)。
protect:其派生类可以访问,但是外部不可访问。
单继承:只继承自一个类。
一个派生类对象包含多个组成部分:
一个含有派生类自己定义的(非静态)成员的子对象。
以及一个与该派生类继承的基类对应的子对象,如果有多个基类,那么这样的子对象也有多个。
派生类到基类的类型转换:我们能将公有(public)派生类型的对象绑定到基类的引用或指针上。
首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员。
除非我们特别指出,否则派生类对象的基类部分会像数据成员一样执行默认初始化。
如果基类定义了一个静态成员,则在整个继承体系中只存在该成员的唯一定义。
如果想将某个类作为基类,则该类必须已经定义而非仅仅声明。
一条声明语句的目的是令程序知道某个名字的存在,以及该名字表示什么样的实体。如一个类一个变量等。派生列表以及与定义有关的其他细节,必须与类的主体一起出现,不能在单独的派生类声明语句中。
C++11新标准提供了一种防止继承发生的方法,即在类名后跟一个关键字final。
静态类型:编译时就知道了。变量声明时的类型或表达式生成的类型。
动态类型:运行时才知道。变量或表达式表示的内存中的对象的类型。
例如 派生类型绑定到基类的引用或指针时,则它的静态类型就和动态类型不一致。如果表达式既不是引用也不是指针,它的静态和动态类型永远一致。
不能从派生类隐式转换到基类,因为编译器是通过检查指针或引用的静态类型来判断该转换是否合法的。可以使用以下方法来实现强转:
dynamic_cast:请求一个类型转换,该转换的安全检查将在运行时执行。
static_cast:强制覆盖编译器的检查工作。
当一个派生类对象给基类对象初始化和赋值时,只有派生类中的基类部分会被拷贝、移动和赋值。它的派生类部分会被切掉。(类定义拷贝控制成员,就可以直接将派生类对象转换成基类对象)

2.虚函数:

使用基类的指针或引用调用一个虚函数成员,会执行动态绑定。
绑定哪个版本的虚函数,由实参的动态类型(实际类型)决定的。
调用非虚函数,会在编译时进行绑定。
当虚函数的返回类型是类本身的指针或引用时,不要求派生类和基类的返回类型一致。派生类可以返回基类类型的指针或引用。
C++11新标准可以使用关键字override标记某个函数,说明派生类中的虚函数。
如果把某个函数指定为final,则之后任何尝试覆盖此函数的操作都会引发错误。
final和override说明符都写在形参列表(包括const和引用修饰符)以及尾返回值之后。
基类和派生类的虚函数定义的默认实参最好一致,因为默认实参的实参值由静态类型决定。
使用作用域运算符可以回避动态绑定的机制。基类::虚函数

3.抽象基类:

纯虚函数:声明语句的分号前加: =0 (可以在类外部为纯虚函数提供函数体)
抽象基类:含有(或未经覆盖直接继承)纯虚函数的类。不能创建抽象基类的对象。

4.访问控制与继承:

派生类的成员和有源只能访问派生类对象中的基类部分和受保护成员。对于普通的基类对象中的成员不具有特殊的访问权限。
某个类对其继承来的成员的访问权限受到两个因素影响:该成员的访问说明符和派生类的派生列表中的访问说明符。
派生类访问说明符对于派生类的成员(及友元)能否访问其直接基类的成员没有影响。对基类成员的访问权限只与基类中该成员的访问权限有关。
派生类访问说明符的作用:控制外部访问,派生类对象中的基类成员。例如public:int a; 为基类中的成员,派生类访问说明符为private,则派生类对象中的a就是private的了。
如果D继承自B,E继承自D:

  • 只有D公有地继承自B,用户代码才能使用派生类向基类的转换。
  • 无论D以什么方式继承自B,D的成员函数和友元都能使用派生类向基类转换。
  • 如果D继承B的方式是受保护的,则E的成员和友元可以使用D->B,私有的则不行。

不能继承友元关系;每个类负责控制各自成员的访问权限。有一个情况说明下:D继承自B,F是B的友元类,F可以访问D中继承自B的成员。
通过在派生类中使用using,可以把基类和间接基类中的可访问成员标记出来。这些成员对于派生类的用户的访问权限,由该成员前的访问说明符决定。
public: using Base::n; // 基类中成员n,对于派生类用户来说是公有的(不管Base类中n是否是protect的)
默认类的继承访问说明符:class是private的,struct是public的。

5.继承中的类作用域:

派生类的作用域嵌套在其基类的作用域之内。(一个名字在派生类作用域不发正确解析,编译器会去基类继续查找)
一个对象、指针或引用的静态类型决定了该对象那些成员是可见的。
派生类也能重用定义在其直接基类或间接基类的名字。派生类的成员将隐藏同名的基类成员。即使派生类成员和基类成员的参数列表不一致。
可以通过作用域运算符来使用一个被隐藏的基类成员。
如果基类与派生类的虚函数接受的时长不同,那么派生类将隐藏基类的虚函数。

6.构造函数与拷贝控制

delete一个动态绑定的指针可能出现指针的静态类型与被删除对象的动态类型不符的情况。
需要有一个虚析构函数来动态绑定析构函数。如果基类的析构函数不是虚函数,那么delete一个派生类对象的基类指针将产生未定义的行为。
虚析构函数将阻止合成移动操作(但是可以在基类中定义)。
先执行最底层的基类的构造函数。
析构函数先执行派生类的析构,继续执行其直接基类的隐式析构函数。
如果基类中的默认构造函数、拷贝构造函数、拷贝赋值运算符或析构函数,是被删除的函数或不可访问的,则派生类对应的成员将是被删除的。
当派生类定义了拷贝或移动操作时,该操作负责包括基类部分成员在内的整个对象。
如果想拷贝或移动机类部分,必须在派生类的构造函数初始化列表中显示的使用基类的拷贝或移动构造函数。
无论基类的构造函数或赋值运算符是自定义还是合成版本,派生类对应的操作都能使用它们。
派生类析构函数只负责销毁由派生类自己分配的资源。
如果构造函数获悉构函数调用了某个虚函数走,我们应该执行与构造函数或析构函数所属类型相对应的虚函数版本。因为此时派生类(或基类)可能是未完成的状态。
在C++11新标准中,派生类能够重用其直接基类定义的构造函数。
不能继承默认、拷贝和移动构造函数,如果派生类没有直接定义这些构造函数,则编译器将合成它们。
using Base::Base;
通常情况,using声明语句只是令某个名字在当前作用域内可见,而当用于构造函数时,将令编译器产生代码。
如果派生类含有自己的数据成员,则这些成员将被默认初始化。
不管using声明出现在哪儿,基类的私有构造函数在派生那种还是一个私有构造函数。访问级别不变。
当一个基类构造函数还有默认实食,这些实参并不会被继承。派生类将获得多个继承的构造函数,每个构造函数分别省略掉一个,还有默认实参的形参 。
如果派生类定义的构造函数与基类的构造函数具有相同的参数列表,该构造函数将不会被继承。
派生类对象被赋值给基类对象时,派生类的部分会被切掉,因此容器和存在继承关系的类型无法兼容。可以在容器中存放智能指针来解决这个问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值