c++ 虚函数和纯虚函数

时间:2022-09-08 07:55:51

一、总结

1.

为了实现C++的多态,C++使用了一种动态绑定的技术。这个技术的核心是虚函数表,每个包含了虚函数的类都包含一个虚表。
我们知道,当一个类(A)继承另一个类(B)时,类A会继承类B的函数的调用权。所以如果一个基类包含了虚函数,那么其继承类也可调用这些虚函数,换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。
虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针。需要指出的是,普通的函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。
虚表内的条目,即虚函数指针的赋值发生在编译器的编译阶段,也就是说在代码的编译阶段,虚表就可以构造出来了。

具体见:https://blog.csdn.net/primeprime/article/details/80776625

在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的。从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现。通过这样的方法,就可以将对象的行为抽象化。

2.虚函数(impure virtual),C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现。子类可以重写父类的虚函数实现子类的特殊化。

3.纯虚函数(pure virtual),C++中包含纯虚函数的类,被称为是“抽象类”。抽象类不能使用new出对象,只有实现了这个纯虚函数的子类才能new出对象。

 C++中的纯虚函数更像是“只提供申明,没有实现”,是对子类的约束,是“接口继承”。

 C++中的纯虚函数也是一种“运行时多态”。

纯虚函数
 1.当在基类中不能为虚函数给出一个有意义的实现时,可以将其声明为纯虚函数,其实现留待派生类完成。 
 2.纯虚函数的作用是为派生类提供一个一致的接口。
 3.纯虚函数不能实例化,但可以声明指针。

纯虚函数在基类只声明不用定义,继承类必须实现。

4.虚函数和纯虚函数对比:
4.1.虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class)。

4.2.虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义。

4.3.虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。

4.4.虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。

4.5.虚函数的定义形式:virtual{method body} 纯虚函数的定义形式:virtual { } = 0; 在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-time bind),而且被两者修饰的函数生命周期(life recycle)也不一样。

4.6.如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类继承重载以后,根据要求调用其子类的方法。

5.非类的成员函数不能定义为虚函数,类的成员函数中静态成员函数和构造函数也不能定义为虚函数,但可以将析构函数定义为虚函数。实际上,优秀的程序员常常把基类的析构函数定义为虚函数。因为,将基类的析构函数定义为虚函数后,当利用delete删除一个指向派生类定义的对象指针时,系统会调用相应的类的析构函数。而不将析构函数定义为虚函数时,只调用基类的析构函数。

实例化时:先调用基类构造函数,然后子类构造函数;释放时:先子类析构函数,然后基类析构函数

#ifndef BASE_H
#define BASE_H class Base
{
public:
Base();
~Base();
virtual void test()=;
}; #endif // BASE_H
#include "base.h"
#include <QDebug>
Base::Base()
{
qDebug()<<"Base()";
} Base::~Base()
{
qDebug()<<"~Base()";
}
#ifndef C1_H
#define C1_H
#include "base.h" class C1:public Base
{
public:
C1();
~C1();
void test() override;
}; #endif // C1_H
#include "c1.h"
#include<QDebug>
C1::C1()
{
qDebug()<<"C1()";
} C1::~C1()
{
qDebug()<<"~C1()";
} void C1::test()
{
qDebug()<<"C1::test";
}

Base()

C1()

~Base()

基类析构函数声明为虚函数后,执行结果

Base()

C1()

~C1()

~Base()

6.只需要在声明函数的类体中使用关键字“virtual”将函数声明为虚函数,而定义函数时不需要使用关键字“virtual”。

7.当将基类中的某一成员函数声明为虚函数后,派生类中的同名函数(函数名相同、参数列表完全一致、返回值类型相关)自动成为虚函数。

8.如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数。在以该类为基类的派生类中,也不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数。

9.构造函数不能声明为虚函数

构造函数一般是用来初始化对象的,因而只有在一个对象生成之后才能发挥多态作用。虚函数表在构造函数调用之后才建立,因而构造函数不可能声明为虚函数。虚函数的调用需要虚函数表指针,而该指针存放在对象的内存空间中;
若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址来调用虚函数。

10.静态成员函数不能是虚函数

静态成员函数对于每一个类只有一份代码,所有的对象共享这份代码,它不归某个对象所有,所以没有动态绑定的必要性,不能被继承,只属于该类

11.内联函数不能是虚函数

内联函数在程序编译的时候展开,在函数调用处进行替换,虚函数是进行动态绑定的

#pragma once
class Shape
{
public:
Shape();
virtual ~Shape(); void Draw1();
virtual void Draw2();
virtual void Draw3()=;
};
#include "Shape.h"
#include <iostream> using namespace std;
Shape::Shape()
{
cout << "shape 构造函数" << endl;
} Shape::~Shape()
{
cout << "shape 析构函数" << endl;
} void Shape::Draw1()
{
cout << "shape Draw1 画个图形" << endl;
}
void Shape::Draw2()
{
cout << "shape Draw2 画个图形" << endl;
}
#pragma once
#include "Shape.h"
class Rectangle:public Shape
{
public:
Rectangle();
~Rectangle(); void Draw1();
void Draw2();
void Draw3();
};
#include "Rectangle.h"
#include <iostream> using namespace std;
Rectangle::Rectangle()
{
cout << "Rectangle 构造函数" << endl;
} Rectangle::~Rectangle()
{
cout << "Rectangle 析构函数" << endl;
} void Rectangle::Draw1()
{
cout << "Rectangle Draw1 画个矩形" << endl;
}
void Rectangle::Draw2()
{
cout << "Rectangle Draw2 画个矩形" << endl;
} void Rectangle::Draw3()
{
cout << "Rectangle Draw3 画个矩形" << endl;
}
#pragma once
#include "Shape.h"
class Circle:public Shape
{
public:
Circle();
~Circle();
void Draw1();
void Draw2();
void Draw3();
};
#include "Circle.h"
#include <iostream> using namespace std;
Circle::Circle()
{
cout << "Circle 构造函数" << endl;
} Circle::~Circle()
{
cout << "Circle 析构函数" << endl;
}
void Circle::Draw1()
{
cout << "Circle Draw1 画个圆形" << endl;
}
void Circle::Draw2()
{
cout << "Circle Draw2 画个圆形" << endl;
} void Circle::Draw3()
{
cout << "Circle Draw3 画个圆形" << endl;
}
// ConsoleApplication2.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include<iostream>
#include<memory>
#include "Shape.h"
#include "Rectangle.h"
#include "Circle.h" using namespace std;
void Run()
{ //Shape *_c;
//_c = new Rectangle();
//_c->Draw1();
//_c->Draw2(); //_c = new Circle();
//_c->Draw1();
//_c->Draw2(); //delete _c; shared_ptr<Shape> _s(new Rectangle());
_s->Draw1();
_s->Draw2();
_s->Draw3();
_s.reset(new Circle());
_s->Draw1();
_s->Draw2();
_s->Draw3(); } int main()
{ Run(); system("pause");
return ;
}

运行结果:

shape 构造函数
Rectangle 构造函数
shape Draw1 画个图形
Rectangle Draw2 画个矩形
Rectangle Draw3 画个矩形
shape 构造函数
Circle 构造函数
Rectangle 析构函数
shape 析构函数
shape Draw1 画个图形
Circle Draw2 画个圆形
Circle Draw3 画个圆形
Circle 析构函数
shape 析构函数
请按任意键继续. . .

Qt 使用智能指针 Pro  添加CONFIG += c++11;

智能指针属于std, std::shared_ptr<Shape> _s(new Rectangle());

c++ 虚函数和纯虚函数的更多相关文章

  1. C&plus;&plus; - 虚基类、虚函数与纯虚函数

    虚基类       在说明其作用前先看一段代码 class A{public:    int iValue;}; class B:public A{public:    void bPrintf(){ ...

  2. C&plus;&plus; Primer--虚函数与纯虚函数的区别

    首先:强调一个概念 定义一个函数为虚函数,不代表函数为不被实现的函数. 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数. 定义一个函数为纯虚函数,才代表函数没有被实现. 定义纯虚函数是为了实 ...

  3. C&plus;&plus; 虚函数与纯虚函数

    #include<iostream> #include<string> using namespace std; class A{ public: virtual void f ...

  4. c&sol;c&plus;&plus; 基金会&lpar;七&rpar; 功能覆盖,虚函数,纯虚函数控制

    1.功能覆盖 ClassA , ClassB ,其中ClassB继承ClassA 类的定义如下面的: #ifndef _CLASSA_H #define _CLASSA_H #include < ...

  5. C&plus;&plus; 虚函数 、纯虚函数、接口的实用方法和意义

    也许之前我很少写代码,更很少写面向对象的代码,即使有写多半也很容易写回到面向过程的老路上去.在写面向过程的代码的时候,根本不管什么函数重载和覆盖,想到要什么功能就变得法子的换个函数名字,心里想想:反正 ...

  6. 【C&plus;&plus;】C&plus;&plus;中的虚函数与纯虚函数

    C++中的虚函数 先来看一下实际的场景,就很容易明白为什么要引入虚函数的概念.假设我们有一个基类Base,Base中有一个方法eat:有一个派生类Derived从基类继承来,并且覆盖(Override ...

  7. C&plus;&plus;中虚函数和纯虚函数的区别与总结

    首先:强调一个概念 定义一个函数为虚函数,不代表函数为不被实现的函数. 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数. 定义一个函数为纯虚函数,才代表函数没有被实现. 定义纯虚函数是为了实 ...

  8. C&plus;&plus;虚函数与纯虚函数用法与区别(转载)

    1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class) ...

  9. C&plus;&plus;(九)— 虚函数、纯虚函数、虚析构函数

    1.虚函数 原因:通过指针调用成员函数时,只能访问到基类的同名成员函数.在同名覆盖现象中,通过某个类的对象(指针及引用)调用同名函数,编译器会将该调用静态联编到该类的同名函数,也就是说,通过基类对象指 ...

随机推荐

  1. ldataset 与 list 的使用

    [WebMethod(Description = @"根据时间查询会议项目[时间格式为:2014-01-01] DateTime StartTime , DateTime Endtime & ...

  2. DOS功能的调用

    DOS功能的调用:主要包含三方面的子程序:设备驱动(基本I/O),文件管理和其他(包括内存管理,自取时间,自取终端向量,总之程序等)随着DOS版本的升级,这种DOS功能调用的子程序数量也在不断的增加, ...

  3. PHP生成条形码

    前阵子在做一个商家优惠券的功能,需要用到条形码,于是将资料重新整理下. 1.什么是条形码? 百度百科定义:条形码(barcode)是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息 ...

  4. &lbrack;Firefly引擎&rsqb;&lbrack;学习笔记二&rsqb;&lbrack;已完结&rsqb;卡牌游戏开发模型的设计

    源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...

  5. WIA Property Constant Definitions

    (@http://msdn.microsoft.com/en-us/library/windows/desktop/ms630202(v=vs.85).aspx): const DeviceID = ...

  6. PlateSpin备份服务器时SQL Server的一些活动信息

      以前写过一篇文章IO is frozen on database xxx, No user action is required", 主要是介绍PlateSpin在服务器层面做DR备份时 ...

  7. 【javascript】上拉下拉弹窗实现

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. Scrapy实战篇(七)之爬取爱基金网站基金业绩数据

    本篇我们以scrapy+selelum的方式来爬取爱基金网站(http://fund.10jqka.com.cn/datacenter/jz/)的基金业绩数据. 思路:我们以http://fund.1 ...

  9. &lpar;转载&rpar;Peter Norvig:十年学会编程

    作者 Peter Norvig 是计算机科学家,Google 的研究总监.在本文中,Peter Norvig会告诉你:为什么急功近利地学习软件开发技术是没效果滴? ================华丽 ...

  10. js 倒计时 (时分秒版本)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...