面向对象程序设计(一) 继承与虚函数

面向对象程序设计(一) 继承与虚函数

基础概念

继承:继承是一种类之间的层级关系。通常在层级关系的根部有一个基类,其它类则直接地或间接地从基类继承而来,继承得来的类叫做派生类

虚函数:基类希望派生类相对于基类自定义自己的合适的版本的函数。在基类中用virtual关键字来定义,同时在派生类中也隐式的是虚函数。任何非静态函数都可以是虚函数

类派生列表 :指明派生类是由哪个基类得来的。基类有一个逗号隔开的基类列表表示。使用了public关键字的派生类对象可以直接当做基类对象去使用。

动态绑定:函数参数列表中传入对象形式为基类对象的引用,但是最终在函数内使用的对象类型由实参决定。传入实参是那种类型的对象,调用的就是对应派生类/基类的对象。这种绑定成为动态绑定。基类中使用virtual关键字实现对虚函数在运行时的动态绑定。

受保护的:派生类能访问基类的共有成员、受保护成员,和其他的外部类一样不能访问私有成员。派生类的成员函数或是友元函数只能访问派生类中基类部分的成员,而不能访问基类对象的protected成员。

类派生列表的限定修饰符:如果是public,那么基类的公有类接口在派生类中,因此我们可以将派生类的对象绑在基类的引用或是指针上

派生类中的虚函数:如果派生类中没有重写基类中的虚函数,那么将会直接继承。如果要显示注明重写方法,在函数形参列表后面(就是const或&限定符后面)加上一个override关键字

派生类到基类的类型转换:因为派生类包含所有基类的成员(对于私有成员,只包含但是不能访问,想要访问只能用getter和setter方法),所以可以将派生类的对象绑在基类的引用或是指针上,完成一次类型转换。只有派生类公有地继承基类时,才可使用类型转换。不存在基类到派生类的类型转换。

派生类的初始化:注意,每个类控制其自己的成员初始化,因此派生类不能直接初始化基类的成员,尽管我们可以访问和修改基类中public 和 protected的部分,但是必须在构造函数中调用基类的构造函数去初始化基类的那一部分。注意必须先初始化基类的成员部分,如classB 继承自基类classA :

classB(int a,int b,int c):classA(a,b),C(c){};

静态成员函数:静态成员函数不可以被派生类重写,但是可以被派生类对象正常调用。

基类与派生类的声明:派生类声明时不能写基类列表,且必须在基类已经被定义之后才能声明。

final关键字:防止继承发生。final修饰的类不可以作为基类。

动态类型与静态类型:当派生类通过将对象绑定到基类的引用或指针的时候,静态类型是基类,动态类型直到运行的时候程序才知道是派生类。但是对于不是指针引用的表达式,静态类型与动态类型永远相同。

对象之间不存在隐式类型转换:尽管不能直接将基类用派生类直接赋值(初始化),但是我们仍然可以通过在基类中定义构造函数接收派生类的拷贝,或是重载积累中的赋值运算符来完成。但是只有派生类中的基类部分能被赋值(初始化),派生类独有部分(或是其他基类)就会被切掉。如代码:

Bulk_quote bulk;
Quote item(bulk);	//通过Quote::Quote(const Quote&)构造函数实现
item = bulk;		//通过Quote::operate=(const Quote&)重载赋值运算符实现

虚函数

​ 对虚函数调用的解析只能在运行时通过传入的形参类型去解析到底调用哪个类中的虚函数,但是这种动态绑定只有在我们使用对象的引用或是指针去调用虚函数的时候才有可能发生(毕竟不知道动态类型是什么)

派生类中的虚函数

1.必须保证形参一致性

  1. 必须保证返回的类型一致,只有在基类虚函数返回自己基类的指针或引用时,派生类虚函数可以返回自己的或是基类的指针或引用
  2. override语句要求必须保证派生类中的虚函数形式与基类中完全一致,否则报错。
  3. 对于默认实参,最好保证基类中与派生类中的默认实参一致
  4. 回避调用虚函数的机制:用作用域符运算限定使用的函数版本
  5. 如果派生类中虚函数需要调用基类中的虚函数版本,那么就需要作用域限定运算符回避动态绑定。

抽象基类

​ 含有纯虚函数的类是抽象基类。纯虚函数明确告诉用户这个函数是没有意义的,而是在这个包含纯虚函数的抽象基类之上定义派生类,并且根据派生类对应的策略去重写(override)这个纯虚函数,从而实现这个功能。抽象基类是不可以实例化的。

定义方法:在形参列表后面加入’= 0’即可定义纯虚函数。

派生类的构造函数:只能通过调用上一层的基类(直接基类)的构造函数进行初始化。初始化一个派生类是一个递归调用构造函数的过程。

抽象基类的作用是定义对事物操作的通用概念,而并非具体策略

具体来说,关键问题不仅是不知道应该如何定义net_price,而是我们根本不希望用户创建一个Disc_quote对象。Disc_quote类表示的是一本打折书籍的通用概念,而并非某种具体的折扣策略。

// 定义一个书本的折扣策略,Disc_Quote定义的是一个打折的概念,由它的各种派生类(Bulk_Quote)去实现具体的打折策略
class Quote
{
public:
    Quote() = default;
    Quote(std::string &book, double sales_price):bookName(book),bookPrice(sales_price){}
    std::string isbn(){return bookName;}
    virtual double net_price(std::size_t n) const {return n * bookPrice;}
    ~Quote() = default;
private:
    std::string bookName;
protected:
    double bookPrice = 0.0;
};

class Disc_Quote : public Quote
{
public: 
    Disc_Quote() = default;
    Disc_Quote(std::string &book, double sales_price, std::size_t Quality, double Discount): Quote(book,sales_price),quality(Quality),discount(Discount){}
    virtual double net_price(std::size_t n) const = 0; 

protected:
    std::size_t quality = 0;
    double discount = 0.0;
};

class Bulk_Quote : public Disc_Quote
{
    Bulk_Quote() = default;
    Bulk_Quote(std::string &book, double sales_price, std::size_t Quality, double Discount):Disc_Quote(book,sales_price,Quality,Discount){}
    virtual double net_price(std::size_t n) const override
    {
        if(quality > 10){
            return discount * Quote::net_price(n);
        }
        else return Quote::net_price(n);
    }
};

访问说明符:访问说明符不影响派生类成员(友元)访问基类成员。对于基类成员的访问权限只与基类中的访问说明符有关。但是它控制派生类的对象对于基类成员的访问权限。

其实就是相当于继承时的访问说明符将派生类中继承来的基类成员的访问最高权限变成了说明符指示的权限。

类的设计和受保护的成员:基类的成员应该被设计分为三组,其接口成员函数仍然需要声明成公有的;其希望其派生类访问的成员应声明成受保护的;只希望可以被基类成员函数和友元函数访问的应当被 声明为私有的。

友元不可以被继承:每个类负责控制各自成员函数的访问权限

用using语句单独设置成员可访问属性:成员可访问性与using所在访问限定符的作用区域有关

默认继承保护级别:class继承的默认是私有的;struct继承的默认是公有的。

继承中的类作用域

编译时的名字查找:动态绑定时,一个派生类的指针或引用被绑定在基类上时,这个静态类型为基类的对象是访问不到派生类中的成员的。因为在对这个函数进行名字查找是从基类开始的。显然基类中不包含这个名字。(名字检查优先于类型检查,对于派生类的成员函数而言,·

名字冲突导致隐藏基类成员:派生类的成员将隐藏基类的成员。可以通过作用域限定符来强制访问隐藏的基类成员。

名字查找只会在本身和直接基类中递归地查找

名字。(名字检查优先于类型检查,对于派生类的成员函数而言,·

名字冲突导致隐藏基类成员:派生类的成员将隐藏基类的成员。可以通过作用域限定符来强制访问隐藏的基类成员。

名字查找只会在本身和直接基类中递归地查找

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值