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

前言 

  • 面向程序设计基于三个基本概念:数据抽象、继承和动态绑定。继承和动态绑定可以使得程序定义与其他类相似但是不完全相同的类;使用彼此相似的类编写程序时候,可以在一定程度上忽略掉他们的区别。

OOP概述

  • oop(面向程序的设计)核心思想是数据抽象、继承和动态绑定。使用数据抽象可以将类和接口实现分离;使用继承,可以定义相似的类型并对相似的类型关系进行建模;使用动态绑定,可以一定程度上忽略相似的内容,而采用统一的方式使用他们的对象。

继承

  • 通过继承联系在一起的类构成了一种层次关系。层次关系的根部叫做基类,其他类则直接或者间接从基类中继承来,通过继承得到的类叫做派生类。基类负责定义在层次关系中所有类共同具备的成员,而每一个派生类则会定义各自特有的属性。

  • 如果派生类想改变基类中包含的特定的函数,根据自己的实际情况进行定制化的开发,基类需要将这个函数声明为虚函数。派生类需要使用类派生列表,明确指出他是从哪个或者哪些基类派生出来的。类派生的形式是:首先一个分号,后面紧跟着以逗号分隔的基类列表,其中每个基类的前面都可以有访问说明符。
#include<iostream>
#include "Sales_item.h"
using namespace std;class Quote{
public:string  isbn() const;virtual double net_price(size_t n) const;
};
class Bulk_quote : public Quote{//Bulk_quote继承了Quote
public:double net_price(size_t n) const override;
};int main(){return 0;
}
  • 派生类必须在其内部对于所有重新定义的虚函数进行声明。派生类显示的注明它将使用哪个成员函数改写基类的虚拟函数,具体的措施是在该函数的形参列表之后增加一个override的关键字。

动态绑定

  • 使用动态绑定可以使用相同的代码分别处理基类和派生类。

double print_total(ostream &os,const Quote &item,size_t n){//计算并且打印给定数量的某种书籍所得的费用//根据传入item的形参的对象类型调用Quote::net_price//或者使用Bulk_quote::net_pricedouble ret = item.net_price(n);os << "ISBN: " << item.isbn() << " # sold: " << n << "total due :" << ret << endl;return ret;
}
  • 可以使用如下两种的调用方式,因为下面的函数运行版本是由实参决定的,即在运行的时候选择函数的版本,所以动态绑定有时候又被称作运行时绑定。在使用基类的引用(或指针)调用一个虚函数的时候将会发生动态绑定。
//basic的类型是Quote;bulk的类型是Bulk_quote类型
print_total(cout,basic,20);//调用Quote的net_price
print_total(cout,bulk,20);//调用Bulk_quote的net_price

定义基类和派生类

  • 完成对于Quote的定义
  • 继承关系中根节点的类通常会定义一个虚析构函数,即使该函数不执行任何实际操作也需要定义析构函数。
class Quote{
public:Quote() = default;Quote(const string &book,double sales_price):bookNo(book),price(sales_price){}string  isbn() const{return bookNo};//返回给定数量的书籍的销售总额//派生类负责改写并且使用不同的折扣计算方法virtual double net_price(size_t n) const{return n * price;}virtual ~Quote() = default;//对于析构函数进行动态绑定
private:string bookNo;//书籍的ISBN序列号
protected:double price = 0.0;//代表普通状态下不打折的价格
};

成员函数和继承

  • 基类需要将两种成员函数区别开来:1,基类希望派生类进行覆盖的函数;2,基类需要派生类直接继承但是不要改变的函数。对于前者,基类通常将其定义为虚函数,在使用指针或者引用调用虚拟函数的时候,该调用将会动态绑定。根据引用或者指针所绑定的对象不同,该引用可能会执行基类或者执行某个派生类。
  • 任何构造函数之外的非静态函数都可以是虚函数。关键字virtual只可以出现在类内部的声明之前而不能用于对于类外部的函数的定义。对于基类声明的虚函数,则该函数在派生类中隐式地也是虚函数。成员函数没有被声明为虚函数,那么解析过程发生在编译的时候而不是运行的时候;即虚函数解析过程发生在运行的时候,动态绑定,动态执行。

访问控制与继承

  • 派生类可以继承定义在基类中的成员,但是派生类成员不一定具有对于从基类继承而来的成员的访问权。派生类可以访问公有成员但是不能访问私有成员。protected用于派生类可以访问,但是禁止其他用户进行访问。

定义派生类

  • 派生类必须通过派生列表明确指出它是从哪个(哪些)基类派生而来的。类派生的形式是:首先一个分号,后面紧跟着以逗号分隔的基类列表,其中每个基类的前面都可以有访问说明符之一,比如public、protected和private。
  • 派生类必须将其继承而来的成员函数中需要覆盖的那些函数重新声明
  • 大多数类都继承自一个类,这种机制叫做单继承

派生类中的虚函数

  • 派生类经常(但不是总是)覆盖定义它继承的虚函数,如果派生类中没有覆盖基类中的缪一个虚函数,派生类会直接继承自基类中的对于虚函数的定义。
  • 在形参列表后面、或者在const成员函数的const关键字后面、或者在引用成员函数的引用限定符后面添加一个关键字override。

派生类对象以及派生类向基类的类型转换

  • 派生类所具有的对象,不仅包含继承自基类的对象还具有自己新创建的对象。但是在一个对象中,继承自基类的部分和派生类自定义的部分不一定是连续存储的,就是在物理存储逻辑上,二者不一定占据连续一段存储空间。
    Quote item; //基类对象Bulk_quote bulk;  //派生类对象Quote *p = &item;  //p指向item(Quote)对象p = &bulk;  //p指向bulk(Bulk_quote)对象的Quote部分Quote *r = &bulk; // r绑定到bulk(Bulk_quote)对象的Quote部分
  • 上面这段代码通常称之为派生类到基类的类型转换,这个过程是由编译器隐式执行的。即可以将把派生类对象或者派生类对象的引用在需要基类引用的地方。同样可以将派生类对象的指针用在需要基类指针的地方。

派生类构造函数

  • 尽管派生类对象中含有从基类继承而来的成员,但是派生类不可以直接初始化这些成员,派生类需要使用基类的构造函数来初始化他的基类部分。派生类对象的基类部分与派生类对象的自己的数据成员都是在构造函数的初始化阶段执行初始化操作的。

  • 派生类将自己继承自基类的那部分交由基类进行初始化,然后初始化派生类自己定义的成员,最后运行派生类的构造函数。除非我们特定指出,否则派生类对象的基类部分会像数据成员一样被默认初始化。但是如果想使用其他的基类构造函数,需要以类名加圆括号的实参列表的形式为构造函数提供初始化数值。这些实参会帮助编译器决定使用哪个构造函数来初始化派生类对象的基类部分。

派生类使用基类的成员

  • 派生类可以访问基类的公有成员和受保护的成员。即派生类的作用域嵌套在基类的作用域之内。即对于派生类的成员来说,使用派生类的成员和使用基类成员的方式是没有不同的。

继承与静态成员

  • 基类定义了一个静态成员,则在整个继承体系中只存在该成员的唯一定义。从基类中派生出多少个派生类,对于每个静态成员来说都只存在唯一的实例。

派生类的声明

  • 派生类的声明需要包含类名但是不包含他的派生列表。

被用作基类的类

  • 如果要将某一个类用作基类,则该类必须已经定义而不是简简单单的声明。
  • 一个类不可以派生它本身。即一个类是基类,同时他也可以是一个派生类。比如D1是Base的派生类,而D2又是D2的派生类。那么Base是D1的直接基类,同时也是D2的间接基类。
  • 对于最终的一个派生类来说,他会包含直接基类的所有成员和每一个间接基类的所有对象。

防止继承的发生

  • 防止自定义的类被继承,那么就在类的名字后面跟上一个关键字final。
  • class NoDerived final{} //NoDerived不能作为基类

类型转换与继承

  • 常规情形下,将引用/指针绑定到一个对象上,则引用或者指针的类型应该与对象的类型是一致的。或者对象的类型含有一个可以接受的const类型转换规则。存在继承关系的类是一个特殊的存在。
  • 基类的指针或者引用绑定到派生类的对象上,有一层很重要的含义就是不确定被绑定的对象的真实类型是基类还是派生类。

静态类型与动态类型

  • 表达式的静态类型是编译的时候已知的,是变量声明时的类型或则表达式生成的类型;动态类型是变量或者表达式表示内存中的对象类型,动态类型直到运行时才可知。
  • 如果表达式既不是指针也不是引用,那么他的动态类型和静态类型是一致的。

不存在从基类向派生类的隐式类型转换

  • 因为一个基类对象是派生类中的一部分,所以不存在基类向派生类的类型转换,因为基类不可以访问派生类中不存在的成员。
  • 既是一个基类指针或者引用绑定在一个派生类度向上,也不可以执行从基类到派生类的转换。
  • 如果基类中存在多个虚拟函数,在进行类型转换的时候,可以使用dynamic_cast请求一个类型转换,该转换的安全检查将会在运行的时候进行检查。如果我们已经知道将某个基类装换为派生类是安全的,可以使用static_cast来强制覆盖掉编译器的检查工作。

在对象之间不存在类型转换

  • 使用派生类对象为一个基类对象进行初始化或者赋值的时候,只有派生类对象中的基类部分会被拷贝、移动和赋值;他的派生类部分将会被忽略掉。

 

 

 

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/447072.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内存池的线程安全问题

malloc/free 据说老版本libc 有俩个版本&#xff0c;当你连接 pthread库的时候它就链接的是线程安全版&#xff0c;否则不是。在glic 2.2 以上无论怎么都是线程安全的。 new/delete new/delete 封装的 malloc/free , 如果malloc/free 是它们就是线程安全的。

C++11命名空间的using说明

std::cin 表示从标准输入读取内容&#xff0c;此处的作用域操作符::是指编译器应该从左侧名字所示的作用域中寻找右侧那个名字。因此std::sin表示使用命名空间std中的cin。 每个名字都需要有独立的using的声明 每一个using声明引入命名空间中的一个成员&#xff0c;比如可以将…

c语音的一些特殊关键字

PRETTY_FUNCTION C语言中获取函数名 C语言中的__LINE__用以指示本行语句在源文件中的位置信息

C++ primer三章二节标准库类型string

标准库类型string 标准库类型string表示可变长的字符序列&#xff0c;使用#include<string>引入头文件&#xff0c;string定义在命名空间std中。 定义和初始化string对象 如何初始化类的对象是由类的本身决定的&#xff0c;类可以定义很多初始化对象的方式&#xff0c;…

vim 不常见但好用的命令

● 跳跃 ○ 向前跳跃是 f ○ 向后跳跃是 F ● 继续 ○ 保持方向是 ; ○ 改变方向是 , ● 可以加上 [count] 来加速 ● ^ 是到本行第一个非空字符 ● 0 是到本行第一个字符&#xff0c;不管是不是空格 ● g_ 是到本行最后一个非空字符 ● 两个按键要依次按下 ● $ 跳到本行最后…

加密机组会 会议纪要

2020年9月28日 1&#xff0c;使用基类继承的机制&#xff0c;调用写好的函数接口 1&#xff0c;不要 使用Content&#xff08;封装数据&#xff0c;本质是一个json字符串&#xff09;&#xff0c;1&#xff0c;因为每次使用这个需要对里面的内容进行序列化&#xff0c;转化成…

c++为什么没有垃圾回收

垃圾回收 内存清理的另一个方面是垃圾回收。在支持垃圾回收的环境中&#xff0c;程序员几乎不必显式地释放与对象关联的 内存。运行时库会在某时刻自动清理没有任何引用的对象。 与C#和Java不一样&#xff0c;在C语言中没有内建垃圾回收。在现代C中&#xff0c;使用智能指针管理…

C++ Vecctor容器浅析

Vector的定义 向量&#xff08;Vector&#xff09;是一个封装了动态大小数组的顺序容器&#xff08;Sequence Container&#xff09;。跟任意其它类型容器一样&#xff0c;它能够存放各种类型的对象。可以简单的认为&#xff0c;向量是一个能够存放任意类型的动态数组。vector…

C++primer第二章2.4节对于const限定符相关内容进行详解

const限定符 const对象一旦创建后其数值就不会被再次改变&#xff0c;因此const对象必须初始化。const对象只在文件中有效在不同的文件中使用不同的const来定义不同的常量&#xff0c;那么每个文件定义的变量只会在自己所属的文件中有效。如果想让多个文件共享同一个const变量…

二分法的常见问题

mid(leftright)/2; mid (high - low) / 2 low; 这样写可以防止left right溢出 ,不过数足够大是时候该溢还是溢 为什么要取右边中间数呢&#xff1f;这是因为在区间里 只有 2 个元素的时候&#xff0c;把[left…right]划分成[left…mid - 1]和[mid…right]这两个区间&#x…

演示IPFS的一个完整的流程以及针对部分概念的详解

整体的流程 1&#xff0c;创建ipfs节点 通过ipfs init在本地计算机建立一个IPFS节点本文有些命令已经执行过了&#xff0c;就没有重新初始化。部分图片拷贝自先前文档&#xff0c;具体信息应以实物为准 $ ipfs init initializing IPFS node at /Users/CHY/.ipfs generating 2…

c++ 算法的时间复杂度

一般ACM或者笔试题的时间限制是1秒或2秒。 在这种情況下&#xff0c;C代码中的操作次数控制在 10^7为最佳。 下面给出在不同数据范国下&#xff0c;代码的时间复杂度和算法该如何选择&#xff1a; 1.n≤ 30,指数级别&#xff0c;dis剪枝&#xff0c;状态压缩dp 2.n < 100 &g…

简单工厂模式实现计算器

#include <iostream> #include <vector> #include <string> #include <iostream> #include <map> using namespace std; #define __THROW_ZERO do {cerr << "The dividend is 0" << endl; exit(1);}while(0);/* 简单工厂处…

TDengine安装教程

TDengine安装教程 前言 TDengine的安装十分简单&#xff0c;可以有以下三种安装方式&#xff0c;源码安装、通过Docker容器进行安装、通过安装包进行安装。但是使用源码安装较为复杂&#xff0c;通过docker的方式最为简单&#xff0c;但是需要一定docker相关的知识&#xff0…

C++中size_t的学习

size_t的定义 size_t是一种数据相关的无符号类型&#xff0c;它被设计得足够大以便能够存储内存中任意对象的大小。设计 size_t 就是为了适应多个平台&#xff0c;size_t等效于unsigned short int 或者 unsigned long int 类型&#xff0c;这个过程是动态匹配的。在需要通过数…

策略模式解决商店打折问题

#include <bits/stdc.h> using namespace std; /*策略模式解决商店打折问题*/class Cashsuper { private:/* data */ public:virtual double addcash(double cash) 0;double Getresult(double money){return addcash(money);} };class Cashnormal : public Cashsuper {p…

android 软件首次运行时引导页左右滑动效果

很多手机软件在安装后首次运行都会进入到引导页面&#xff0c;再次运行时会进入到主页面。 多了不说了&#xff0c;先看效果图&#xff1a; 代码如下&#xff1a; main.xml <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:an…

C++中size_type类型详解

介绍 是和string类类型和vector类类型定义相关的类型&#xff0c;用以保存任意string对象或vector对象的长度&#xff0c;标准库类型将size_type定义为unsigned类型string抽象意义是字符串&#xff0c; size&#xff08;&#xff09;的抽象意义是字符串的尺寸&#xff0c; str…

单一职责原则 实现贪吃蛇代码的封装

单一职责原则(SRP),就一个类而言&#xff0c;应该仅有一个引起它 变化的原因。 一个c语言的贪吃蛇代码 如何使用单一职责原则封装成c面向对象呢 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include<stdlib.h> #include <wi…

android ProgressBar实现扫描SD卡文件 + SimpleAdapter绑定ListView

代码 activity_main.xml <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:layout_width"match_parent"android:layout_height"match_parent"to…