多态也是C++中的一个重要的方面。多态和动态类型,虚函数本质上是指同样的事情。





1 虚函数

类中的成员函数原型前面加上virtual 表面这个函数是个虚函数。虚函数的目的是为了在继承它的派生类中又一次定义这个函数,以便于通过基类的指针或引用在执行时对派生类的函数进行调用。

2 派生类和虚函数

派生类普通情况下要重定义所继承的虚函数。有几个注意事项。

<1>虚函数的声明必须和基类中的函数声明原型全然一致。例外的是当基类返回基类型的指针或者引用的时候。派生类能够派生类类型的指针或者引用

<2>virtual 关键词加不加无所谓,可是一般要加上,能看的清楚这是个虚函数

<3>virtual的标签一旦在基类中的函数前面加上。就永远也去不掉





3 动态绑定的触发

C++的函数调用默认不使用动态绑定。使用动态绑定须要两个必须的条件

<1>通过基类类型的指针或者引用进行函数的调用

<2>函数是虚函数

通过这里也能够说明,当通过指针或者引用调用函数的时候,即便派生类重定义了基类的版本号。也不会调用派生类的版本号。由于它不会触发多态。

4 虚函数的覆盖机制

有时候通过指针或者引用不想触发多态。此时能够通过加限定符来显式的调用基类的函数

  1. Derived d;
  2. Base *p = &d;
  3. p->Base::func();

假设在派生类中的函数定义的时候想要调用基类的同名函数那么也要这样显式的调用,否则会引发无穷递归。





5 虚函数跟复制控制

<1>通常将析构函数设置为虚函数,这是由于析构函数在运行的时候仅仅会运行自身的部分。

比如通过基类的指针或者引用来调用析构函数的话,那么仅仅会调用基类的构造函数。派生类的构造函数将不会被调用。

<2>构造函数不能定义为虚函数,由于在构造函数是在对象全然构造之前执行的。在构造函数执行的时候,对象的动态类型还不完整。

<3>赋值操作符原理上能够设置为虚函数。可是这样做是没意义的。原因例如以下:

虚函数要求函数原型全然一致,这当中包含了函数參数类型的一致,假设我们把operator = 设置为虚函数。那么在基类中函数的參数是基类类型,在派生类虚函数的參数类型依旧是基类类型。可是赋值操作符是要求函数參数类型和类类型一致。这样就会产生很easy混淆的东西。

  1. class Base{
  2. public:
  3. virtual Base& operator =(Base& xx);
  4. }
  5.  
  6. class Derived : public Base
  7. {
  8. public:
  9. virtual Derived& operator =(Base& xx); //virtual function
  10. Derived& operator =(Derived& xx); //real operator =
  11. }

6 构造函数和析构函数中的虚函数

在执行构造函数和析构函数的时候。此时的对象都不是一个完整的类。这个时候编译器将对象的对象视作在构造期间发生了变化,假设在当中调用虚函数的话。那么调用的将会是类自身类型





定义的版本号。

原因:基类在构造的时候,派生类部分的成员还没有初始化,假设此时调用的是派生类的虚函数,那么派生类的虚函数訪问类成员就会出问题。

同理基类的析构函数也是这样子。由于析构函





数直管自身成员的释放,派生类在调用析构函数的时候先调用自身的析构函数。然后再去调用基类的析构函数。所以基类的析构函数不可能调用派生类的虚函数,由于派生类的成员已经被释





放。实比例如以下:

  1. //Base.h
  2. #pragma once
  3. #include <iostream>
  4. using namespace std;
  5.  
  6. class Base
  7. {
  8. public:
  9. Base(void){ func();};
  10. ~Base(void){ funp();};
  11. virtual void func(){cout<<"this is in Base constructor"<<endl;}
  12. virtual void funp(){cout<<"this is in Base destructor"<<endl;};
  13. };
  1. //Derived.h
  2. #pragma once
  3. #include "base.h"
  4. class Derived :
  5. public Base
  6. {
  7. public:
  8. Derived(void){func();}
  9. ~Derived(void){func();}
  10. virtual void func(){cout<<"this is in Derived constructor"<<endl;}
  11. virtual void funp(){cout<<"this is in Derived destructor"<<endl;};
  12. };
  1. //main.cpp
  2. #include "Derived.h"
  3.  
  4. void main()
  5. {
  6. Base X;
  7. Derived Y;
  8. }

7 纯虚函数

虚函数的形參后面跟上“=0”则表示纯虚函数。纯虚函数须要注意的有下面几点:

<1>纯虚函数没有函数体;

<2>最后面的“=0”并不表示函数返回值为0,它仅仅起形式上的作用。告诉编译系统“这是纯虚函数”;

<3>这是一个声明语句,最后应有分号。

<4>函数不能被调用

<5>不能创建有纯虚函数的类对象

<6>假设在一个类中声明了纯虚函数,而在其派生类中没有对该函数定义。则该虚函数在派生类中仍然为纯虚函数。





含有一个或者多个纯虚函数的类被称为抽象类。抽象类不能创建对象。抽象类的意义在于提供功能基础接口。然后由派生类来实现,在此基础上实现多态。可是抽象类能够作为引用或者指针。

实例程序例如以下:

  1. //Base.h
  2. #pragma once
  3. #include <iostream>
  4. using namespace std;
  5.  
  6. class Base
  7. {
  8. public:
  9. Base(void){ func();};
  10. ~Base(void){ funp();};
  11. virtual void func(){cout<<"this is in Base constructor"<<endl;}
  12. virtual void funp(){cout<<"this is in Base destructor"<<endl;};
  13. virtual void fuck()=0;
  14. };
  1. //Derived.h
  2. #pragma once
  3. #include "base.h"
  4. class Derived :
  5. public Base
  6. {
  7. public:
  8. Derived(void){func();}
  9. ~Derived(void){func();}
  10. virtual void func(){cout<<"this is in Derived constructor"<<endl;}
  11. virtual void funp(){cout<<"this is in Derived destructor"<<endl;};
  12. virtual void fuck(){cout<<"what a fuck day it is!"<<endl;}
  13. };

  1. //main.cpp
  2. #include "Derived.h"
  3.  
  4. void main()
  5. {
  6. Derived Y;
  7. Base& X = Y;
  8. }

C++primer读书笔记11-多态的更多相关文章

  1. 机器学习实战 - 读书笔记(11) - 使用Apriori算法进行关联分析

    前言 最近在看Peter Harrington写的"机器学习实战",这是我的学习心得,这次是第11章 - 使用Apriori算法进行关联分析. 基本概念 关联分析(associat ...

  2. 强化学习读书笔记 - 11 - off-policy的近似方法

    强化学习读书笔记 - 11 - off-policy的近似方法 学习笔记: Reinforcement Learning: An Introduction, Richard S. Sutton and ...

  3. Key Technologies Primer 读书笔记,翻译 --- Struct 学习 1

    原文链接:https://struts.apache.org/primer.html 本来想写成读书笔记的,结果还是变成翻译,谨作记录,学习.   1.HTML -- 见我前面文章 2.Interne ...

  4. 《精通Spring 4.X企业应用开发实战》读书笔记1-1(IoC容器和Bean)

    很长一段时间关注在Java Web开发的方向上,提及到Jave Web开发就绕不开Spring全家桶系列,使用面向百度,谷歌的编程方法能够完成大部分的工作.但是这种不系统的了解总觉得自己的知识有所欠缺 ...

  5. c++ primer 读书笔记

    顺序容器:为程序提供控制元素存储和访问顺序的能力,这种顺序与元素加入到容器时的位置相对应,而与元素值无关. 另外还有根据关键字的值来存储元素的容器:有序.无序关联容器. 另外STL还有三种容器适配器, ...

  6. c++ primer读书笔记之c++11(四)

    1  带有作用域的枚举 scoped-enumeration 相信大家都用过枚举量,都是不带有作用域的,在头文件中定义需要特别注意不要出现重名的情况.为了解决这种问题,c++11提供了带作用于的枚举. ...

  7. c++ primer读书笔记之c++11(三)

    1 =default构造函数限定符 c++11针对构造函数提供了=default限定符,可以用于显式指定编译器自动生成特定的构造函数.析构或赋值运算函数.参考代码如下: class CtorDftTy ...

  8. c++ primer读书笔记之c++11(二)

    1 新的STL模板类型,std::initializer_list<T> c++11添加了initializer_list模板类型,用于提供参数是同类型情况的可变长度的参数传递机制,头文件 ...

  9. c++ primer读书笔记之c++11(一)

    1 新的关键词nullptr c++11引入新的关键词nullptr,用于表示空指针,用于替换之前c提供的NULL(最初NULL是定义在stdlib中的宏定义,通常是0). 2 新的别名定义机制 al ...

  10. C++ Primer 读书笔记:第11章 泛型算法

    第11章 泛型算法 1.概述 泛型算法依赖于迭代器,而不是依赖容器,需要指定作用的区间,即[开始,结束),表示的区间,如上所示 此外还需要元素是可比的,如果元素本身是不可比的,那么可以自己定义比较函数 ...

随机推荐

  1. python--(常用模块-3-正则表达式)

    python--(常用模块-3-正则表达式) 正则表达式是对字符串操作的⼀种逻辑公式. 我们⼀般使⽤正则表达式对字符串进⾏匹 配和过滤. 使⽤正则的优缺点: 优点: 灵活, 功能性强, 逻辑性强. 缺 ...

  2. python--(常用模块-1)

    python--(常用模块-1) 一.模块的简单认识: 什么是模块,模块就是我们把装有特有功能的代码进行归类的结果,从代码编写的单位来看我们的程序,从小到大的顺序:一条代码<语句块<代码块 ...

  3. 【codeforces 810A】Straight «A»

    [题目链接]:http://codeforces.com/contest/810/problem/A [题意] 有n门课的成绩,和一个整数k代表每门课的满分都是k分; 然后这n门课的成绩是按照平均分算 ...

  4. JavaScript正則表達式知识汇总

    Js 正則表達式知识汇总 正則表達式: 1.什么是RegExp?RegExp是正則表達式的缩写.RegExp 对象用于规定在文本中检索的内容. 2.定义RegExp:var +变量名=new RegE ...

  5. POJ 2447

    挺水的一题.其实只要理解了RSA算法,就知道要使用大整数分解的方法来直接模拟了. 不过,要注意两个INT64的数相乘来超范围 #include <iostream> #include &l ...

  6. POJ3624 Charm Bracelet 【01背包】

    Charm Bracelet Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22621   Accepted: 10157 ...

  7. Linux下永久改动MAC地址和ifconfig命令总结

    1. 固定一个MAC地址,特别是在使用多个虚拟机的时候 linux环境下: 用root身份登录,在/etc/rc.d/rc.local里加上这三句 ifconfig eth0 down ifconfi ...

  8. 使用Networkx进行图的相关计算——黑产集团挖掘,我靠,可以做dns ddos慢速攻击检测啊

    # -*- coding: utf-8 -*- import networkx as nx import matplotlib.pyplot as plt iplist={} goodiplist={ ...

  9. Linux下iscsi的使用

    查看是否已安装了iscsi-initiator:  [root@test\ ~]# rpm -qa |grep iscsi iscsi-initiator-utils-6.2.0.868-0.18.e ...

  10. 带你玩转Visual Studio——带你理解多字节编码与Unicode码

    目录(?)[-] 多字节字符与宽字节字符 char与wchar_t string与wstring string 与 wstring的相关转换 字符集Charcater Set与字符编码Encoding ...