AMD

1、虚函数和纯虚函数

定义一个函数为虚函数,不代表函数为不被实现的函数。
定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。
定义一个函数为纯虚函数,才代表函数没有被实现。
定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。

 

虚函数只能借助于指针或者引用来达到多态的效果。(用基类的指针或引用指向派生类,如果基类中定义了虚函数,并且在派生类中重写了这个虚函数,就可以通过这个指针或者引用实现多态)

 

 

虚函数为了重载和多态的需要,在基类中是由定义的,即便定义是空,所以子类中可以重写也可以不写基类中的函数!

纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数!

 

 

两者的区别在于编译器何时去寻找所要调用的具体方法,对于重载而言,在方法调用之前,编译
器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;而对于多态,只有等到方法调用的那一刻
,编译器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

 

二、引入原因

  1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
  2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
  为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。
声明了纯虚函数的类是一个抽象类。所以,用户不能创建类的实例,只能创建它的派生类的实例。
纯虚函数最显著的特征是:它们必须在继承类中重新声明函数(不要后面的=0,否则该派生类也不能实例化),而且它们在抽象类中往往没有定义。
定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。
纯虚函数的意义,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。所以类纯虚函数的声明就是在告诉子类的设计者,“你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。

 

 

有纯虚函数的类是抽象类,不能生成对象,只能派生。他派生的类的纯虚函数没有被改写,那么,它的派生类还是个抽象类。
定义纯虚函数就是为了让基类不可实例化化
因为实例化这样的抽象数据结构本身并没有意义。
或者给出实现也没有意义
实际上我个人认为纯虚函数的引入,是出于两个目的
1、为了安全,因为避免任何需要明确但是因为不小心而导致的未知的结果,提醒子类去做应做的实现。
2、为了效率,不是程序执行的效率,而是为了编码的效率。

 

2、c++怎么实现接口

接口就是可供外部调用的方法,实现就是对这些方法的编程实现。
接口是公开的,实现是封装的,外面不知道实现是怎么样的。

所谓的接口,即将内部实现细节封装起来,外部用户用过预留的接口可以使用接口的功能而不需要知晓内部具体细节

有两种方法(https://blog.csdn.net/TAOKONG1017/article/details/79561856)

1、一种是将对象的实现细目隐藏于指针背后,简单的说就是将其分成两个类,一个类只提供接口,另一个负责实现该接口,这种设计手法常称为Pimpl Idiom(pointer to implementation)。

  定义一个接口类,在这个接口类里面的私有成员函数定义另外一个类的指针,在这个接口类的实现中通过这个类的指针去调用接口函数。

  定义另外一个类,在这个类当中做具体的功能函数实现。 

2、另一种方法就是将接口定义为抽象类,接口全被定义为纯虚函数(纯虚函数没有具体的实现方法),派生类的成员函数负责实现这些接口。这种设计手法称为Object Interface。千万不要忘记把抽象接口类的析构函数定义为virtual函数,可能会造成内存泄漏。

(C++将析构函数定义成virtual的真正原因:1、A* d = new B();(假定A是基类,B是从A继承而来的派生类),那么其(A类)析构函数必须是虚的,否则在delete d时,B类的析构函数将不会被调用,因而会产生内存泄漏和异常;2、在构造一个类的对象时,先构造其基类子对象,即调用其基类的构造函数,然后调用本类的构造函数;销毁对象时,先调用本类的析构函数,然后再调用其基类的构造函数。。。。。。。基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。所以,将析构函数声明为虚函数是十分必要的。。。。。。。只要能够保证继承关系中最高的基类的析构函数是虚的,那么就不会产生前面所谈及的问题)

 

硬件一样,你写的软件比人家慢,你认为会有哪些方面的原因

从硬件和软件两方面回答

1、硬件方面,软件控制硬件通过读写寄存器的方式,一方面有没有可能提高接口的速率,如果是I2C接口,可以提高i2c速率,二是对于寄存器的读写方式,看看spec,是不是有更快的模式

2、软件方面,a、查看逻辑上有没有问题,是不是做了某些不必要的操作  b、查看是不是可以在流程上做一些优化,比如把某些操作做到开机中去,而不必每次打开硬件都要做这些操作

   c、看log,把一些频繁打印的非必要的log去掉  d、一些不相关的操作通过多线程的方式去做,通过空间换取时间

 

如果出现内存泄露问题怎么排查

1、首先进行版本回朔,看是从哪个版本开始出现的内存泄露问题。

2、查代码,安卓原生的代码肯定不会存在内存泄露问题,一般都是人为写出来的,一般有三种原因

  a、分配完内存后没有释放

  b、代码逻辑有问题,造成内存没有办法回收

  c、分配和释放的代码放的地方不对,比如放在构造函数和析构函数里面,如果不是

 

1) 为什么delete pb;不会调用派生类的析构函数呢?

因为这里的析构函数是非虚函数,通过指针访问非虚函数时,编译器会根据指针的类型来确定要调用的函数;也就是说,指针指向哪个类就调用哪个类的函数,这在前面的章节中已经多次强调过。pb 是基类的指针,所以不管它指向基类的对象还是派生类的对象,始终都是调用基类的析构函数。

2) 为什么delete pd;会同时调用派生类和基类的析构函数呢?

pd 是派生类的指针,编译器会根据它的类型匹配到派生类的析构函数,在执行派生类的析构函数的过程中,又会调用基类的析构函数。派生类析构函数始终会调用基类的析构函数,并且这个过程是隐式完成的,这在《C++析构函数》一节中已经讲到了。

将基类的析构函数声明为虚函数后,派生类的析构函数也会自动成为虚函数。这个时候编译器会忽略指针的类型,而根据指针的指向来选择函数;也就是说,指针指向哪个类的对象就调用哪个类的函数。pb、pd 都指向了派生类的对象,所以会调用派生类的析构函数,继而再调用基类的析构函数。如此一来也就解决了内存泄露的问题。

在实际开发中,一旦我们自己定义了析构函数,就是希望在对象销毁时用它来进行清理工作,比如释放内存、关闭文件等,如果这个类又是一个基类,那么我们就必须将该析构函数声明为虚函数,否则就有内存泄露的风险。也就是说,大部分情况下都应该将基类的析构函数声明为虚函数。

 

posted @ 2018-08-28 16:05  knightsoul  阅读(92)  评论(0)    收藏  举报