虚函数

【总结】虚函数小结

自作多情 提交于 2019-12-12 01:42:50
虚函数小结 1) 虚函数是动态绑定的,也就是说,使用虚函数的指针和引用能够正确找到实际类的对应函数,而不是执行定义类的函数。这是虚函数的基本功能,就不再解释了。 2) 构造函数不能是虚函数。而且,在构造函数中调用虚函数,实际执行的是父类的对应函数,因为自己还没有构造好, 多态是被disable的。 3) 析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的(delete 指向子类的父类指针时,不会析构子类)。 4) 将一个函数定义为纯虚函数,实际上是将这个类定义为抽象类,不能实例化对象。(与第5点结合) 5) 纯虚函数通常没有定义体,但也完全可以拥有。 6) 析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。 7) 非纯的虚函数必须有定义体,不然是一个错误。 8) 派生类的override虚函数定义必须和父类完全一致。除了一个特例,如果父类中返回值是一个指针或引用,子类override时可以返回这个指针(或引用)的派生。例如,在上面的例子中,在Base中定义了 virtual Base* clone(); 在Derived中可以定义为 virtual Derived* clone()。可以看到,这种放松对于Clone模式是非常有用的。 9)基类private纯虚函数:在C++中virtual表示多态,public

C++对象之内存(有继承)

我的梦境 提交于 2019-12-11 17:36:14
上一篇 介绍了C++对象在无继承的各种情况下的内存空间占用情况和内存布局,这一篇来讨论有继承时的情况。有继承的情况会复杂一些,本文会分为四个层次讨论:单一继承无多态、单一继承有多态、多重继承和虚拟继承。本文中的一些图片和一些观点来自《深入探索C++对象模型》,它C++对象的底层原理讲解得非常详细,强烈安利! 如无特别说明,以下代码均在64位机器上的VS2019运行 在C++继承模型中,子类是自己的成员与父类的成员的总和。但标准并没有规定子类和父类的排列顺序,大部分编译器将父类放在子类前面,但virtual base class除外,后面会讨论到。 单一继承无多态 一、空类 class Concrete1 { }; class Concrete2 : public Concrete1 { }; 输出sizeof(Concrete2)结果是1。原因和无继承时的空类大小为1的原因一样,空类的实例也需要有独一无二的内存地址,因此空类被赋予了1字节。 二、非空类 内存布局 在没有虚函数时,类的内存布局和C语言的struct完全一样。 代码测试 代码测试一下: class Point2d { protected: float _x, _y; }; class Point3d : public Point2d { protected: float _z; }; //main函数中: cout <

C++中的 虚函数 纯虚函数 虚基类(virtual)

China☆狼群 提交于 2019-12-11 10:34:55
前言 :需要了解三者的区别,必须要掌握多态的三个必要条件: 继承 重载 父类指针指向子类对象。 虚函数 纯虚函数 虚基类三者区别 1.虚函数是用于多态中virtual修饰父类函数,确保父类指针调用子类对象时,运行子类函数的。 2.纯虚函数是用来定义接口的,也就是基类中定义一个纯虚函数,基类不用实现,让子类来实现。 3.虚基类是用来在多继承中,比如菱形继承中,如果两个父类继承自同一个类,就只实例化一个父类 ①虚函数 第一个是没有使用多态(只用继承)的一般实现方式: class A { public: void printf(){ cout<<"printf A"<<endl; } }; class B : public A { public: void printf(){ cout<<"printf B"<<endl; } }; int main(int argc, const char * argv[]) { A *a = new A(); a->printf(); B *b = new B(); b->printf(); return 0; } 结果: printf A printf B 这是早期没有多态的代码,缺点:代码冗余 下面是使用了多态但是没有引用virtual关键字的情况: int main(int argc, const char * argv[]) { A *a =

漫谈C++为什么不支持模板虚函数

半世苍凉 提交于 2019-12-10 17:54:56
首先,我们要区分模板类虚函数和模板虚函数,话不多说,先上代码: 模板类虚函数 template<class T> class A { public: virtual ~A(){} virtual void foo(T &t){} }; template<class T, class R> class B : public A<T> { R *r; public: void foo(T &t) override{} }; 模板虚函数 class A { public: virtual ~A(){} template<class T> virtual void foo(T &t){} }; class B : public A { public: template<class T> void foo(T &t) override{} }; 显然模板虚函数是编译不过的,至于为什么,我们可以深究至C++多态的实现原理,就能知道为什么C++不允许定义模板虚函数了。 我们知道C++的多态是通过虚表实现的,对于含有虚函数的类,会为其定义一个虚表,每个实例化的对象都有一个指向该虚表的指针,所以同样的类,含有虚函数的类的实例大小比不含虚函数的多上一个指针的大小,虚表里为每个虚函数维护着一条跳转记录,这些跳转地址在编译期就被确定了,存放在类定义模块的数据段中,在程序运行期是不可修改的

C++之多态性与虚函数

北城以北 提交于 2019-12-10 04:03:01
  面向对象程序设计中的多态性是指向不同的对象发送同一个消息,不同对象对应同一消息产生不同行为。在程序中消息就是调用函数,不同的行为就是指不同的实现方法,即执行不同的函数体。也可以这样说就是实现了“一个接口,多种方法”。   从实现的角度来讲,多态可以分为两类:编译时的多态性和运行时的多态性。前者是通过静态联编来实现的,比如C++中通过函数的重载和运算符的重载。后者则是通过动态联编来实现的,在C++中运行时的多态性主要是通过虚函数来实现的,也正是今天我们要讲的主要内容。   1.不过在说虚函数之前,我想先介绍一个有关于基类与派生类对象之间的复制兼容关系的内容。它也是之后学习虚函数的基础。我们有时候会把整型数据赋值给双精度类型的变量。在赋值之前,先把整形数据转换为双精度的,在把它赋值给双精度类型的变量。这种不同类型数据之间的自动转换和赋值,称为赋值兼容。同样的,在基类和派生类之间也存在着赋值兼容关系,它是指需要基类对象的任何地方都可以使用 公有 派生类对象来代替。为什么只有公有继承的才可以呢,因为在公有继承中派生类保留了基类中除了构造和析构之外的所有成员,基类的公有或保护成员的访问权限都按原样保留下来,在派生类外可以调用基类的公有函数来访问基类的私有成员。因此基类能实现的功能,派生类也可以。   那么它们具体是如何体现的呢 ?(1)派生类对象直接向基类赋值,赋值效果

C++ 基础语法 快速复习笔记(3)---重载函数,多态,虚函数

北慕城南 提交于 2019-12-10 02:42:10
1.重载运算符和重载函数: C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。 当您调用一个重载函数或重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。 选择最合适的重载函数或重载运算符的过程,称为重载决策。 a.函数重载: 在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。 b.运算符重载: 您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。 重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。 详细例子: https://www.runoob.com/cplusplus/cpp-overloading.html 2.多态: 多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。 C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。 例子: https://www.runoob

复盘:广州某游戏公司后端校招笔试

我只是一个虾纸丫 提交于 2019-12-10 00:04:55
LN游戏笔试复盘 0. 写在前面 这次笔试应该算是我第一次正式的笔试。这次笔试复盘即使为了查漏补缺,也是为了纪念第一次笔试吧。试题是HR和我约好时间通过邮箱给我发送的,是一份word文档。首先要吐槽一下试卷的格式:竟然是用word文档的方式,现在大家不都是使用第三方公司或者自己研发的考试系统嘛= =。算了看一下题目:一共十道题,时间给了40分钟。其中C++基础概念题(简答题)3道,基础算法题7道。整体来说难度不大,但是题量十分巨大! 1. C++基础概念题 请解释封装、继承和多态。 封装:   封装就是将数据和行为相结合,抽象为一个整体,形成一个类。类中的数据和函数都是类的成员。我们可以使一部分成员充当类与外部的接口,而将其它的成员隐藏起来。这样就限制了外部对成员的访问,也使不同类之间的影响度降低。目的在于将对象的使用者和设计者分开,以提高软件的可维护性和可修改性。 继承:   继承就是新类从已有类那里得到已有的特性。类的派生指的是从已有类产生新类的过程。原有的类成为基类或父类,产生的新类称为派生类或子类。子类继承基类后,可以创建子类对象来调用基类函数,变量等。继承是代码重用的重要工具。 多态:   多态就是一个接口,多种实现。具体体现在调用同一个函数名,可以根据需要实现不同的功能。多态分为两种编译时多态和运行时多态。   静态多态(早绑定、编译时多态、重载)

C++ virtual 析构函数

落花浮王杯 提交于 2019-12-09 22:48:11
C++中虚析构函数的作用 我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?下面用一个小例子来说明: 有下面的两个类: [cpp] view plain copy #include <iostream> using namespace std; class ClxBase { public: ClxBase() {}; virtual ~ClxBase() {cout<<"AAA"<<endl;}; virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; }; }; class ClxDerived : public ClxBase { public: ClxDerived() {}; ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; }; void DoSomething() { cout << "Do something in class ClxDerived!" << endl; }; }; int main() { ClxBase *pTest = new ClxDerived; pTest->DoSomething

DLL导出类避免地狱问题的完美解决方案 (转)

不羁的心 提交于 2019-12-09 11:17:51
备注:在设计导出类时,在网上找到了这篇文章,非常的好,收藏了!网址: http://club.topsage.com/thread-497586-1-1.html DLL动态链接库是程序复用的重要方式,DLL可以导出函数,使函数被多个程序复用,DLL中的函数实现可以被修改而无需重新编译和连接使用该DLL的应用程序。作为一名面向对象的程序员,希望DLL可以导出类,以便在类的层次上实现复用。所幸的是,DLL确实也可以导出类。 然而事实却没这么简单,导出类的DLL在维护和修改时有很多地方必需很小心,增加成员变量、修改导出类的基类等操作都可能导致意想不到的后果,也许用户更新了最新版本的DLL库后,应用程序就再也不能工作了。这就是著名的DLL Hell(DLL地狱)问题。 DLL地狱问题是怎么产生的呢?看下面的例子,假设DLL有一个导出类ClassD1: class ClassD { public: int GetInt(); private: int m_i; }; int ClassD::GetInt() { return m_i; } 应用程序使用现在的代码来使用这个类: ClassD d; printf(“%d”, d.GetInt()); 程序进行正正常,没有什么问题。后来DLL需要升级,对ClassD进行了修改,增加了一个成员变量,如下: class ClassD // 修改后

虚函数实现原理

我是研究僧i 提交于 2019-12-07 18:44:26
转自:https://blog.csdn.net/weixin_40237626/article/details/82313339 前言 C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。 关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家一个清晰的剖析。 当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有举一反三。不利于学习和阅读,所以这是我想写下这篇文章的原因。也希望大家多给我提意见。 言归正传,让我们一起进入虚函数的世界。 虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样