C++_《C++ Primer》 第15章 面向对象程序设计

C++ Primer 15.8容器与继承Basket,Quote,Bulk_Quote类 C++ Primer 15.8容器与继承Basket,Quote,Bulk_Quote类。const &成员函数与&&成员函数,虚函数与继承。 阅读详情

C++_《C++ Primer》 第15章 面向对象程序设计

要点:

如何写C++的多态

virtual和动态绑定

virtual的作用是让实际指向派生类的基类指针能动态调用函数的派生类版本而不是基类,不加virtual关键字调用的就是基类的版本。

其中的原理是指针是静态类型在编译时确定,指向的对象是动态类型在运行时确定,故编译器编译时只能识别到指针的类型是基类,而无法识别到指针指向的实际类型是个派生类,加了virtual声明就能确保识别出动态类型。

c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。

另外可以在virtual方法后面加=0使其变为纯虚函数,声明了纯虚函数的类无法直接定义使用,且其派生类必须实现纯虚函数的方法(跟Java的abstract很像)

struct A{
    virtual void f1(int) const = 0;
};
struct B : A{
	void f1(int) const override{};
};
int main(){
    //A a1; //forbidden
    B b1;
    A *pa1 = &b1; //ok
}
override和final

override声明可用于检查函数是否重载了基类的同名函数(方法名和参数列表要相同), 主要是让编译器检查方法的,但为了可读性建议最好加上。

如果不希望某个类再被继承或者方法再被重载,可用final声明(这不废话吗?)

struct B : A{
	void f1(int) const final; //不允许后继的其他类覆盖f1
}

C++继承体系构建

public,private和protected继承 ⭐⭐⭐

C++相对于Java独特的一点是能使用派生访问继承符来控制继承自派生类的新类对基类的访问权限:

对于继承自基类的类本身及类的使用者,派生访问继承符并不影响其对基类访问权限,类对基类成员的访问权限仍旧看基类的访问说明符。

public继承自基类的类,对于其子类来说基类的访问属性不变(能访问基类所有非private成员),对于子类用户来说可访问public成员,private或protected的基类部分无法访问;

private继承自基类的类,对于其子类来说基类所有属性都是private,无法访问,子类的用户访问权限同public继承;

protected继承自基类的类,对于其子类来说只能访问基类的public部分,子类的用户访问权限同public继承;

演示:

class Base{
    friend class Pal;
    public:
        void pub_mem(){};
    protected:
        int prot_mem;           // protected 成员
    private:
        char priv_mem = 'B';          // private 成员
};
struct Pub_Derv : public Base {
    void f() { 
        Base::pub_mem();
        Base::prot_mem;
        // Base::priv_mem;   //private成员对派生类不可访问
    }
};
struct Priv_Derv : private Base {
    void f1() { 
        Base::pub_mem();
        Base::prot_mem;
        // Base::priv_mem;  //private成员对派生类不可访问
    }
};
struct Prot_Derv : protected Base {
    void f2() { 
        Base::pub_mem();
        Base::prot_mem;
        // Base::priv_mem;  //private成员对派生类不可访问
    }
};

struct Derived_from_Public : public Pub_Derv {
    void use_base() { 
        Base::pub_mem();
        Base::prot_mem;
        // Base::priv_mem;  //private成员对派生类不可访问


    }
};
struct Derived_from_Private : public Priv_Derv {
    void use_base() { 
        // Base::pub_mem();      //从新类来说继承的基类部分全是private
        // Base::prot_mem;
        // Base::priv_mem;


    }    
};
struct Derived_from_Protected : public Prot_Derv {
    void use_base() { 
        Base::pub_mem();    //继承的基类部分全是protected,只有基类和派生类能访问
        Base::prot_mem;
        // Base::priv_mem;
    } 
};

int main(){
    Pub_Derv d1;
    Priv_Derv d2;     
    Prot_Derv d3;
    
    Base *p = &d1;
    // p = &d2;    // 无法指向私有继承的派生类,要是能的话就能借此修改基类数据了,破坏封装性
    // p = &d3;    // 无法将保护继承派生类向基类转换,原因同上

    d1.f();
    d1.pub_mem();
    // d1.prot_mem;     //inaccesiable
    // d1.priv_mem;     //inaccesiable



    Derived_from_Public dd1;
    Derived_from_Private dd2;
    Derived_from_Protected dd3;
    p = &dd1;
    // p = &dd2;   
    // p = &dd3;

    dd1.f();
    dd1.pub_mem();
    dd1.use_base();
    // dd1.prot_mem;    //inaccesiable
    // dd1.priv_mem;    //inaccesiable

    

   /*
        总结:派生访问控制符用来控制后继类对基类部分的访问权限;
        派生类私有或受保护的基类部分不能通过转换为基类的方法修改'
    
    */
}
派生类的构造和销毁

​ C++中派生类并不是一个独立对象,而是派生类部分和基类部分的混合,构造派生类时总是先构造基类部分,再构造派生类部分,自上而下; 构析时则相反,先销毁派生类部分,再构造销毁部分,自下而上。

​ 示例:

#include<iostream>
using namespace std;
class Quote {
    public:
        Quote() = default;
        Quote(const Quote &) = default;             //复制拷贝
        Quote(Quote &&) = default;                  //移动拷贝
        Quote& operator=(const Quote&) = default;   //拷贝赋值
        Quote& operator=(Quote &&) = default;       //移动赋值

        Quote(const std::string &book,double sales_price):bookName(book),price(sales_price){ cout << "Quote contructed with 4 args " << endl; }
        std::string isbn() const {return bookName;}
        //返回给定数量的书籍的销售总额
        //派生类负责改写并使用不同的折扣计算算法 
        virtual double net_price(std::size_t n) const { return n * price; };
         ~Quote() { cout << "Quote destroyed " << endl; };
    private:
        std::string bookName;
    protected:
        double price = 0.0;
};
class Disc_quote : public Quote {
    public:
        Disc_quote() = default;
        Disc_quote(const std::string& book,double price,std::size_t qty,double disc): Quote(book,price), quantity(qty),discount(disc){ cout << "Disc_quote contructed with 4 args " << endl; }
        ~Disc_quote(){ cout << "Disc_quote destroyed " << endl; }
        double net_price(std::size_t);
        std::pair<size_t, double> discount_policy() const { return {quantity,discount}; }
    protected:
        std::size_t quantity = 0;   //折扣适用的购买量
        double discount = 0.0;      // 表示折扣的小数值
};
// 以Disc_quote这个中间类重构Bulk_quote
class Bulk_quote : public Disc_quote {
    public:
        Bulk_quote() = default;
        Bulk_quote(const std::string& book, double price, std::size_t qty, double disc):Disc_quote(book,price,qty,disc){ cout << "Bulk_quote contructed with 4 args " << endl;}
        double net_price(std::size_t) const override;
        ~Bulk_quote(){ cout << "Bulk_quote destroyed " << endl; }
};
double Bulk_quote::net_price(std::size_t n) const{
    int total = price * n;
    if(n < quantity)
        return total * (1 - discount);
    else
        return total;
}
int main(){
    Bulk_quote bulk("Flower for Algernon", 50.00, 5,0.5);   //先自下而上调用,后自上而下构建
    cout << bulk.isbn() << " " << bulk.net_price(10) << endl;

    Bulk_quote *bulkP = &bulk;
    Quote *itemP = &bulk;
    bulkP->discount_policy();
   

}

运行将出现如下结果:

Quote contructed with 4 args
Disc_quote contructed with 4 args
Bulk_quote contructed with 4 args
Flower for Algernon 500
Bulk_quote destroyed
Disc_quote destroyed
Quote destroyed

​

可以将派生类向基类隐式装换,但不允许反过来:

base *bp1,b1;
derived *dp1,d1;

bp1 = &d1; // right
dp1 = &b1; // wrong

​ 此外,在建立有多级派生类的OO体系时最好将基类的构造函数和构析函数声明为虚函数,以便编译器能够正确地构造和析构对象类型。(尤其是当你需要用智能指针处理类的传递时)

更进一步

完成 15.21 的自定义类练习

扩展该章的TextQuery体系 (15.36,可结合正则表达式)

难点:

理解public基础,private基础和protected继承

在C++中管理多级派生类的访问权限,运用三五法则,内存管理(项目实战)

疑问

15.8.1 为什么不能直接使用对象进程而要用指针,直接用对象会有什么后果?

回答:

其一,如果直接用对象的话没法解决派生类的兼容问题 比如给vector<Base>传Derived类会导致Derived类被隐式转为Base类,其Base部分被切掉,这样想在Derived类做的一些处理就无效了

其二,避免不必要的拷贝,节省内存空间,加快运行效率。

扩展参考:https://stackoverflow.com/questions/22146094/why-should-i-use-a-pointer-rather-than-the-object-itself

扩展阅读

对比Java中的abstract与C++中的virtual

C++Primer第15章 面向对象程序设计 面向对象程序设计基于三个基本概念:数据抽象、继承和动态绑定。 15.1 OOP:概述 面向对象程序设计的核心思想: 数据抽象:可以将类的接口与实现分离 继承:可以定义相似的类型并对其相似关系建模 动态绑定:可以在一定程度上忽略相似的类型,以统一的方式使用它们的对象 通过继承关系联系在一起的类构成一种层次关系。通常在层次关系的根部有一个基类,其它类则直接或间接从基类继承而来,这些继承的类称为派生类。 基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自特有的成员。 在C++语言中,基类将类 阅读详情

相关推荐

CS5511芯片实战:如何用DP转LVDS驱动高刷显示器(附配置代码)

本文详细解析了CS5511芯片在DP转LVDS高刷显示器驱动方案中的应用,包括核心架构、寄存器配置、时序优化及典型问题排查。通过实战案例和配置代码,帮助开发者快速实现120Hz高刷新率显示,特别适合医疗影像和工业HMI等严苛场景。

m3n5b7v8c9x的博客 1077

C++ Primer 第十五章 面向对象程序设计 15.8 容器和继承 练习和总结

15.8 容器和继承 在容器中管理继承体系的类时,如果需要多个子类都调用某一个操作,往往保留的是基类的指针,这做会更加的灵活。 练习 15.8 15.29 vector<shared_ptr<Quote>> vec; /*vec.push_back(make_shared<Bulk_quote>("123", 20, 10, 0.9)); vec.push...

大琪子的博客 226

LPC2124定时器实现跑马灯

文章目录一、keil 4新建工程 一、keil 4新建工程 1、Create New Project,注意,最好添加进一个新建好的文件夹里,keil不会帮你新建。 2、添加设备类型:NXP目录下的LPC2124 3、选择启动文件Startu.s 4、new 一个file,保存命名为.c文件 5、添加进project里,右击,add 选中,添加 6、设置编译输出.hex文件 ...

qq_37937830的博客 5753

《C++ Primer》第15章 15.8节习题答案

《C++ Primer》第15章 面向对象程序设计 15.8节 容器与继承 习题答案 练习15.27:重新定义你的Bulk_quote类,令其继承构造函数。 【出题思路】 本题练习继承构造函数的定义。 【解答】 #include <iostream> #include <string> #include <ostream> using namespace std; class Quote { public: Quote() = default;

chenyijun的专栏 3462

Hermes+MemOS记忆系统:用SQLite构建可编辑AI个人认知资产

AI记忆不是简单延长上下文或叠加RAG,而是构建具备分层存储、混合检索与自然语言反馈能力的个人认知资产。其核心原理在于将用户交互痕迹(L1)、偏好策略(L2)、结构化世界模型(L3)和结晶化技能统一管理,实现记忆的可追溯、可修正、可进化。技术价值体现在降低token消耗、提升长程一致性、支持离线演进,并突破传统‘静态快照’式知识库局限。典型应用场景包括个人知识助手、客服工单分析Agent、研发文档理解系统等。本文聚焦Hermes Agent与MemOS深度集成方案,详解SQLite作为ACID底座支撑L1/

weixin_33623304的博客 434

18650电池组流体直冷热管理仿真:COMSOL多物理场耦合建模路线与实战策略

在动力电池与储能系统的热管理设计中,多物理场耦合仿真已成为平衡性能与成本的关键工具。液冷方案的换热极限促使行业转向制冷剂直冷技术,而两相流与电池产热的强耦合特性,使得基于COMSOL的数值建模成为评估系统温升与流动分配的重要手段。工程实践中,电化学-热-流场全耦合模型与集总电池-流场简化模型各有适用场景,前者揭示电池内部微观机理,后者则以高计算效率支撑包级方案迭代。通过合理简化几何、标定内阻与熵热系数、设置等效换热边界,仿真能够准确预测直冷系统在快充或大倍率放电下的温度均匀性与压降特性,从而指导流道优化与制

weixin_33585822的博客 314

C++ Primer(第五版) 15.8--15.8.1节练习

15.28 #include <iostream> #include <vector> #include <memory> #include "Quote.h" using namespace std; int main() { Bulk_quote item("a2", 7.7, 10, 0.2); vector<Quote&g...

wlli1985的博客 280

在 Esp32 摄像头上实现边缘脉冲 FOMO 物体检测

本文将详细介绍如何在 Esp32 摄像头上实现边缘脉冲 FOMO 物体检测,并提供实现源码

DT程序员的博客 706

C++primer 15.8节练习

练习15.28 1 #include <iostream> 2 #include <string> 3 #include <utility> 4 #include <memory> 5 #include <vector> 6 7 using namespace std; 8 9...

weixin_30767835的博客 172

恒玄BES调试笔记-BES2500如何打开COMPEXP

打开Compressor and expander会占用资源,这个根据个人项目需求而定吧!打开方法,以及参数修改如下: config\best2300a_ibrt_anc\target.mk SPEECH_RX_COMPEXP ?= 1 apps\audioplayers\bt_sco_chain_cfg_default.c #if defined(SPEECH_RX_COMPEXP) /*****************************************************

leezs的博客 706

allegro如何设置走线等长

设置走线等长!

weixin_42499230的博客 6935

C++ primer 第15章 面向对象程序设计

文章目录前言OOP:概述继承动态绑定定义基类和派生类定义基类成员函数与继承访问控制与继承定义派生类派生类中的虚函数派生类对象及派生类向基类的类型转换派生类构造函数派生类使用基类的成员继承与静态成员派生类的声明被用作基类的类防止继承的发生类型转换与继承静态类型与动态类型在对象之间不存在类型转换虚函数对虚函数的调用可能在运行时才被解析c++的多态性派生类中的虚函数final和override说明符回避虚函数的机制抽象基类纯虚函数含有纯虚函数的类是抽象基类派生类构造函数只初始化它的直接基类重构访问控制与继承受保护

weixin_43116900的博客 1734

【智能车Code review】——环岛的判定与补线操作

【1】效果展示图 状态1、状态2 状态3 状态4、状态5 状态6 状态7 状态8 【2】状态机设定需要的参数 以右环岛为例: k_left:左线的斜率 rou_of_left:左线的方差 last_rou_of_left:上一帧的左线方差 l_start:左线第一行为黑的所在行 right_turn_down; right_turn_middle; right_turn_up; 左线的下、中、上拐点 fiv_width[i]:第i行的赛道宽度 leftline_du

hanhan的博客 2万+

《c++primer》 第15章:面向对象程序设计

基类是派生类的父类,派生类继承基类的成员。class Base { // 基类public:void print() const { // 打印函数// 输出基类信息class Derived : public Base { // 派生类,公有继承基类public:void print() const { // 重写基类的 print 函数// 输出派生类信息。

qq_40844444的博客 349

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

C++ Primer:第15章 面向对象程序设计

C++ Primer:第15章 面向对象程序设计第15章 面向对象程序设计15.1 OOP:概述15.2 定义基类和派生类1. 定义基类2. 定义派生类3. 类型转换与继承15.3 虚函数15.4 抽象基类15.5 访问控制与继承15.6 继承中的类作用域15.7 构造函数与拷贝控制15.8 容器与继承15.9 文本查询程序再探 第15章 面向对象程序设计 15.1 OOP:概述 面向对象程序设计的核心思想是数据抽象、继承和动态绑定。数据抽象可将类的接口与实现分离,继承可定义相似的类型并对相似关系建模.

北冥有鱼wyh的博客 588

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

C++ Primer 第五版 第15章 面向对象程序设计

面向对象程序设计(object-oriented programming)的核心思想是数据抽象、继承和动态绑定。通过使用数据抽象,我们可以将类的接口与实现分离;使用继承,可以定义相似的类型并对其相似关系建模;使用动态绑定,可以在一定程度上忽略相似类型的区别,而以统一的方式使用它们的对象。任何构造函数之外的非静态函数都可以是虚函数。关键字virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义。如果基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数。

weixin_44500921的博客 1198

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

C++ Primer 第15章 面向对象程序设计 重要知识点

重要知识点 面向对象程序设计(object-oriented programming)的核心思想是数据抽象、继承和动态绑定。 任何构造函数之外的非静态函数都可以是虚函数。 关键字virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义。 派生类能访问公有成员,而不能访问私有成员,也可以访问受保护的(protected)成员。 从派生类向基类的类型转换只对指针或引用类型有效。 如果我们通过基类的引用或指针调用函数,则使用基类中定义的默认实参,即使实际运行的是派生类中的函数...

ddmike2015的博客 290

C++ primer第15章:面向对象程序设计

面向对象程序设计的核心思想是数据抽象、继承和动态绑定。通过数据抽象,可以将类的接口与实现分离;通过继承,可以定义相似的类型并对其相似关系建模;使用动态绑定,可以在一定程度上忽略相似类型的区别,而以统一的方式使用它们的对象。公有继承:public继承,继承类的可见性与基类相同私有继承:private继承,保护继承:protected继承,除了公有继承,其他的很少用,不用过多细究。公有继承 public protected 不可见私有继承 private private 不可见。

weixin_44623330的博客 551

C++ Primer第五版之第15章 面向对象程序设计

第15章 面向对象程序设计 1.OOP概述 为了对不同定价策略进行建模,我们定义了一个Quote基类,其对象表示按原价出售的书籍。其派生出Bulk_quote的基类,表示可以打折出售的书籍。 对于某些函数,基类希望它的派生类自定义适当的版本,此时基类就将这些函数声明为virtual。 通过在形参列表之后增加一个override关键字可以显式注明它将使用了哪个成员函数改写基类的虚函数。 ...

HakunaMatata的博客 267

【C++ primer】第15章 面向对象程序设计 (1)

面向对象程序设计基于 3 个基本概念:数据抽象、继承和动态绑定。

HPP_优快云的博客 533

《C++ Primer》第15章 面向对象程序设计(一)

*面向对象程序设计(object-oriented programming)**的核心思想是数据抽象、继承和动态绑定。我们首先完成Quotepublic:private:// 书籍的ISBN编号protected:// 书籍的原价基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作。public:private:// 适用折扣的最小购买量// 折扣额else {Bulk_quote从Quote继承了isbn函数和bookNoprice。

MaTF_的博客 784

C++Primer学习笔记:第15章、面向对象程序设计

C++Primer学习笔记:第13章、拷贝控制 1、继承和动态绑定对程序的编写有两方面的影响:我们可以更容易地定义与其他类相似但不完全相同的新类;在使用这些彼此相似的类编写程序时,我们可以在一定程度上忽略掉它们的区别。 2、面向对象程序设计的核心思想是数据抽象、继承和动态绑定。通过使用数据抽象,我们可以将类的接口与实现分离;使用继承,可以定义相似的类型并对其相似关系建模;使用动态绑定,可以在一定程度上忽略相似类型的区别,而以统一的方式使用它们的对象。 3、在C++语言中,基类将类型相关的函数与派生类不做改变

weixin_45373355的博客 351

C++ Primer 第五版 学习总结(十五) 第15章 面向对象程序设计

基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。除构造函数之外的任何非静态函数都能定义为虚函数。virtual关键字只能出现在类内部的声明语句之前而不能用于类外部的函数定义。如果基类把一个函数声明为虚函数,则该函数在派生类中隐式地也是虚函数。成员函数如果没有被声明为虚函数,则其解析过程发生在编译阶段而非运行阶段。派生类能访问基类的公有成员,不能访问私有成员。如果基类希望定义外部代码无法访问,但是派生类对象可以访问的成员,可以使用受保护的(protected)访问运算符进行说明。

cf18862344377的博客 856

C++ Primer 第15章:面向对象程序设计

C++面向对象程序设计核心概念摘要 本文介绍了C++面向对象编程(OOP)的三大核心特性:数据抽象(接口与实现分离)、继承(派生类从基类继承成员)和多态(通过虚函数实现动态绑定)。重点展示了基类与派生类的定义方式: 基类需要声明虚析构函数,成员变量使用protected访问控制 派生类通过:public继承基类,构造函数需初始化基类部分 使用override关键字明确重写虚函数 通过基类指针/引用调用虚函数实现多态行为 示例代码演示了Quote/Bulk_quote的继承关系以及Animal/Dog/Cat

qq_40843427的博客 348

《C++ Primer》导学系列:第 15 章 - 面向对象程序设计

在面向对象程序设计中,基类和派生类是实现继承的关键概念。基类(也称为父类或超类)是派生类(也称为子类或衍生类)的基础,派生类从基类继承属性和行为,并可以添加自己的属性和行为。定义一个基类很简单,我们只需定义一个普通的类。基类可以包含数据成员、成员函数、构造函数、析构函数和其他成员。派生类是从基类继承的类。我们使用关键字publicprotected或private来指定继承方式。通常使用public继承,使得基类的公有成员在派生类中仍然是公有的。本节总结。

iShare_Carlos的博客 1048
上一篇: Effective C++ Chapter1 适应C++ 总结(1-4)
下一篇: EffectiveC++ Chapter 2 构造,构析,赋值 总结
另一种开始
博客等级 码龄7年 250粉丝 67原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值