《C++ primer》读书笔记:面向对象程序设计

目录

15.1 OOP(object-oriented programming):概述

本节要点

继承

动态绑定

15.2 定义基类和派生类

定义基类

定义派生类

派生类构造函数

15.3 虚函数

15.4 抽象基类

纯虚函数

抽象基类


15.1 OOP(object-oriented programming):概述

本节要点

  • 数据抽象:将类的接口和实现相分离。
  • 继承:可以定义类似的类型并对其相似关系建模。
  • 动态绑定:可以在一定程度上忽略相似类型的区别,从而以统一的方式使用它们的对象。

继承

基类(base class)→  派生类(derived class)

基类将两种不同的函数区别对待:

  • 类型相关的函数:基类希望他的派生类各自定义适合自己的版本,于是将这些函数声明为虚函数(virtual function)

  • 不做改变直接继承的函数

class Quote
{
public:
	std::string isbn() const;
    //一个基类声明虚函数的实例
	virtual double net_price(std::size_t n) const;
};

派生类必须在其内部对所有需要重新定义的虚函数进行声明(虚函数在派生类中不必再添加virtual关键字,允许派生类显式地注明将使用哪个成员函数改写基类的虚函数,即添加override关键字

class Bulk_quote :public Quote
{
public:
	double net_price(std::size_t n) const override //显式注明改写的虚函数;
};

动态绑定

当我们使用基类引用&指针调用一个虚函数时,将发生动态绑定,以允许我们可以通过同一段代码来同时实现处理不同类(基类和派生类)的对象。

double print(ostream& o, const Quote& item, size) //基类的引用
{
	double ret = item.net_price(n);
	//其他相关代码
}
  • print函数的调用:print函数的形参是基类Quote的引用,但我们既可以使用基类Quote的对象来调用print函数,也可以使用派生类Bulk_quote的对象来调用该函数。
  • net_price函数的调用:另外,由于net_price函数为虚函数,因此将执行动态绑定,通过传入基类引用&item的对象类型来判断item.net_price(n)函数调用的是基类的函数还是派生类的函数,即在运行时选择函数的版本。

15.2 定义基类和派生类

定义基类

  1. 基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。
  2. 任何构造函数之外的非静态函数都可以声明为虚函数。
  3. virtual不能用于类外部函数的定义。
  4. 派生类虽从基类继承而来,但不能访问基类继承来的private成员,只能访问public成员和protected成员。

定义派生类

重申:派生类必须在其内部对所有需要重新定义的虚函数进行声明(虚函数在派生类中不必再添加virtual关键字,允许派生类显式地注明将使用哪个成员函数改写基类的虚函数,即添加override关键字

如果派生类没有覆盖基类中的某个虚函数,那么派生类会直接继承它在基类中的版本。

由于派生类中含有与基类相同的组成部分,因此我们既能把派生类的对象当成基类对象来使用,又能把基类的引用(指针)绑定到派生类对象的基类部分上,这种转换称为派生类到基类的类型转换。

Quote item;
Bulk_quote bulk;
Quote* p = &item; //p指向Quote的对象item
p = &bulk;        //p指向Bulk_quote的对象bulk的Quote部分
Quote& r = bulk;  //r绑定到Bulk_quote的对象bulk的Quote部分

派生类构造函数

派生类不能直接初始化继承来的成员,也必须使用基类的构造函数来初始化他的基类部分

因此,程序中任何能够生成派生类对象的语句,都要说明其包含的基类对象是如何初始化的,如果不做说明,则编译器认为基类对象要用无参构造函数初始化。

在执行一个派生类的构造函数之前,总是先执行基类的构造函数。

在初始化列表中,要指明调用基类构造函数的形式,具体写法:

#include <iostream>
#include <string>
using namespace std;

class CBug 
{
    int legNum, color;
public:
    CBug(int ln, int c1) : legNum(ln), color(c1)
    {
        cout << "CBug Constructor called." << endl;
    };
    ~CBug()
    {
        cout << "CBug Destructor called." << endl;
    }
    void Printlnfo()
    {
        cout << legNum << "," << color << endl;
    }
};
class CFlyingBug : public CBug
{
    int wingNum;
public:
    /*重点在这里*/
    CFlyingBug(int ln, int c1, int wn) : CBug(ln, c1), wingNum(wn) /*重点在这里*/
    {
        cout << "CFlyingBug Constructor called." << endl;
    }
    ~CFlyingBug()
    {
        cout << "CFlyingBug Destructor called." << endl;
    }
};
int main() {
    CFlyingBug fb(2, 3, 4);
    fb.Printlnfo();
    return 0;
}

程序输出结果:
CBug Constructor called.
CFlyingBug Constructor called.
2,3(直接继承了void Printlnfo()函数,不是重新定义的虚函数,因此仍然是只打印2,3两个值,与4无关)
CFlyingBug Destructor called.
CBug Destructor called.

15.3 虚函数

所有的虚函数都必须有定义。

15.4 抽象基类

纯虚函数

将一个函数声明为纯虚函数(pure virtual function)意在表明当前这个函数是没有意义的,与虚函数不同的是,纯虚函数无需被定义。

只需要在函数体的位置(声明语句的分号之前)书写【=0】就可以将一个虚函数说明为纯虚函数。

注意:

  • 【=0】只能出现在类内部的虚函数声明语句处。
  • 允许为纯虚函数提供定义,但必须定义在类的外部。

抽象基类

含有纯虚函数的类称为抽象基类。

通过抽象基类定义接口,用其后续的派生类来覆盖这些接口。因此不能创建抽象基类的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值