C++ 虚函数

2020-05-30  本文已影响0人  水中的蓝天

本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢。

前言:

多态性是面向对象程序设计的重要特征之一,所谓多态性是指当不同的对象收到函数名相同,参数不同时,产生不同的动作。C++的多态性具体体现在运行和编译两个阶段;在程序运行时的多态性通过继承虚函数来体现,而在程序编译时多态性体现在函数运算符重载上。

重载

在C++语言中,只有在声明函数原型时形式参数个数或者对应位置的类型不同,两个或多个的函数共用一个名字。这种在同一个作用域中允许多个函数使用同一函数名的措施被称为重载(overloading)。函数重载是C++程序获得多态性的途径之一。

函数重载要求编译器能够唯一的确定调用一个函数时应执行哪个函数的代码,即采用哪个函数实现。确定函数实现时,要求从函数参数的个数类型上来区分。这就是说,进行函数重载时,要求同名函数在参数个数上不同,或者参数类型不同,否则,将无法实现函数重载。 注意:返回值不同不在考虑范围


int square(int x){
return x*x;
}

double square(double y){
return y*y;
}


以上两个同名函数即为函数重载

----------------------------------------

用重载函数实现求园和矩形的周长:

const double PI = 3.1415;

double length(float r){
return 2*PI*r;
}

double length(float x, float y){
return 2*(x+y);
}

在程序中不可滥用函数重载,不适当的重载会降低程序的可读性。C++语言并没有提供任何约束来限制重载函数之间必须有关联,开发者可能用相同的名字定义两个不相关的函数;实际上函数重载暗示了一种关联,不应该重载那些本质上有区别的函数,只有当函数实现的语义非常接近时才应使用函数重载 !

函数重载的二义性(ambiguity)

  1. 如果函数调用的实际参数类型与一个重载函数的形式参数类型完全匹配,则选择调用该重载函数;
  2. 如果找不到与实际参数类型完全匹配的函数原型,但将一个类型转换为更高级类型后能找到完全匹配的函数原型,编译程序将选择调用该重载函数。所谓更高级类型是指能处理的值域较大,比如:int 转换为 unsigned int; unsigned int 转换为 long;
  3. 隐式类型转换是由C++编译程序自动完成的,这种类型转换是引起函数重载二义性的主要原因。在重载函数中使用默认参数也可能造成二义性

C++还提供了一种更为灵活的多态机制:虚函数

虚函数允许函数调用函数体的联系在运行时才进行,当一般的类型对应与不同的类型变种时,这个能力显得尤其重要!

例如:


类型A,类型B;  B类型是A类型的派生类

A  *p; //指向类型A的对象的指针
A objA; //类型A的对象 objA
B objB; //类型B的对象 objB
p = & objA;  //p可以指向A类型的对象
p = & objB;  //p可以指向B类型的对象

利用指针p, 可以通过objB访问所有从objA继承的元素,但不能用p访问objB自身特定的元素(除非用了显示类型转换)

  1. 在派生类重定义虚函数时必须有相同的函数原型,包括返回值类型、函数名、参数个数、参数类型的顺序都必须相同
  2. 虚函数必须是类的成员函数,不能为全局函数,也不能为静态函数,不能将友员说明为虚函数,但虚函数可以是另一个类的友员

虚函数使用注意

是不是对 构造函数和析构函数 很疑惑 ?

构造函数 示例

block的构造函数 
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc; 
  __Block_byref_age_0 *age; // by ref    // 如果不使用__block 修饰,这里的代码会是 int age; 
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,__Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

析构函数示例

class  CDemo {
public:
     ~CDemo() { //析构函数
     cout << "Destructor called"<<endl;
       }
 };

虚函数和重载函数的比较

扩展---纯虚函数

class 类名{
   virtual 返回值类型 函数名(参数序列) = 0;
}

比如:

class Creature{
   public:
   virtual char *kindOf() = 0;
}

抽象类


cout<<指向派生类的指针->kindOf()<<endl;

上面的代码同时还给出了一种绕过虚函数机制的方法,即使用带有作用域限定符的完全限定函数名

上一篇 下一篇

猜你喜欢

热点阅读