首页 > 其他分享 >虚函数与多态

虚函数与多态

时间:2023-02-04 11:06:08浏览次数:31  
标签:函数 派生类 多态 基类 public 指针


虚函数:

C++中的虚函数的作用主要是实现了多态的机制。

虚函数是一种在基类定义为virtual的函数,并在一个或多个派生类中再定义的函数。虚函数的特点是,只要定义一个基类的指针,就可以指向派生类的对象。

注:无虚函数时,遵循以下规则:C++规定,定义为基类的指针,也能作指向派生类的指针使用,并可以用这个指向派生类对象的指针访问继承来的基类成员;但不能用它访问派生类的成员。

使用虚函数实现运行时的多态性的关键在于:必须通过基类指针访问这些函数。

一旦一个函数定义为虚函数,无论它传下去多少层,一直保持为虚函数。

把虚函数的再定义称为过载(overriding)而不叫重载(overloading)。

 

 纯虚函数:是定义在基类中的一种只给出函数原型,而没有任何与该基类有关的定义的函数。纯虚函数使得任何派生类都必须定义自己的函数版本。否则编译报错。

纯虚函数定义的一般形式:

Virtual type func_name(参数列表)=0;

 含有纯虚函数的基类称为抽象基类。抽象基类又一个重要特性:抽象类不能建立对象。但是抽象基类可以有指向自己的指针,以支持运行时的多态性。

使用虚函数时,有两点要注意,如下所述 。
         1 )只能用 virtual 关键宇声明类的成员函数,使它成为虚函数,而不能将类外的普通函
数声明为虚函数。 因为虚函数的作用是允许在派生类中对基类的虚函数重新定义 。 显然,它
只能用于类的继承层次结构中 。
        2 ) 一个成员函数被声明为虚函数后,在同 一类族中的类就不能再定义一个非 virtua l 的
但与该虚函数具有相同的参数(包括个数和类型)和函数返回值类型的同名函数。

举个例子:

#include<bits/stdc++.h>
using namespace std;
class Base
{
public:
virtual void who()
{
ECHOLN("我是基类!");
}
};

class deriv_1:public Base
{
public:
voidwho()
{
ECHOLN("我是子类deriv_1");
}
};

class deriv_2:public Base
{
public:
void who()
{
ECHOLN("我是子类deriv_2");
}
};

int main()
{
classBase *b,b0;
classderiv_1 d1;
classderiv_2 d2;
b= &b0;
b->who();
b= &d1;
b->who();
b= &d2;
b->who();
return0;
}

输出:

我是基类!
我是子类deriv_1
我是子类deriv_2

 当一个类带有虚函数时 i 编译
系统会为该类构造一个虚函数表,它是一个指针数组,用于存放每个虚函数的人口地址 。 系
统在进行动态关联时的时间开销是很少的,因此,多态是高效的 。

多态

多态的概念

同一种东西在不同场景下的多种形态,当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。比如举个例子:见人说人话,见鬼说鬼话

.动态多态的分类

虚函数与多态_虚函数

静态多态

  静态多态是编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型的转换),可推断出要调用哪个函数,如果有对应的函数就调用该函数,否则出现编译错误。

(1)动态多态的实现条件: 

基类中必须包含虚函数,并且派生类一定要对基类中的虚函数进行重写

通过基类对象的指针或引用调用虚函数

所谓重写是:a.基类函数必须为虚函数,b.派生类函数必须与基类中的虚函数保持一致(包括返回类型、函数名称、参数列表) 但是有例外:即协变:

第一种是:基类虚函数必须返回基类对象的指针/引用,派生类虚函数返回派生类对象的指针/引用   

第二种是:虚拟的析构函数(基类和派生类的析构函数名字不相同)  

第三种是:派生类的虚函数可以与基类中的虚函数访问限定符不一样。

举个例子:

#include<bits/stdc++.h>
using namespace std;
class A
{
public:
void foo()
{
printf("1\n");
}
virtual void fun()
{
printf("2\n");
}
};
class B : public A
{
public:
void foo()
{
printf("3\n");
}
void fun()
{
printf("4\n");
}
};
int main(void)
{
A a;
B b;
A *p = &a;
p->foo();
p->fun();
p = &b;
p->foo();
p->fun();
return 0;
}

虚函数与多态_多态_02

  第一个p->foo()和p->fuu()都很好理解,本身是基类指针,指向的又是基类对象,调用的都是基类本身的函数,因此输出结果就是1、2。
第二个输出结果就是1、4。p->foo()和p->fuu()则是基类指针指向子类对象,正式体现多态的用法,p->foo()由于指针是个基类指针,指向是一个固定偏移量的函数,因此此时指向的就只能是基类的foo()函数的代码了,因此输出的结果还是1。而p->fun()指针是基类指针,指向的fun是一个虚函数,由于每个虚函数都有一个虚函数列表,此时p调用fun()并不是直接调用函数,而是通过虚函数列表找到相应的函数的地址,因此根据指向的对象不同,函数地址也将不同,这里将找到对应的子类的fun()函数的地址,因此输出的结果也会是子类的结果4。

多态缺陷

降低了程序运行效率(多态需要去找虚表的地址) 
空间浪费。

 

 

标签:函数,派生类,多态,基类,public,指针
From: https://blog.51cto.com/u_15952369/6036918

相关文章

  • c++函数
    一,函数基础1.函数一般用一个名字表示,即函数名。返回类型,函数名,参数列表,和函数体构成了函数定义。函数定义的语法形式类型说明符 函数名(含类型说明的形式参数表){ 函数体}......
  • 模拟实现不受限制的字符串函数--C语言版
    C语言中提供了许多十分好用的库函数,一旦我们掌握了它们,我们使用C语言写代码就会变得更加得心应手。1.strlenstrlen函数就是计算字符串的长度的,它会一直读取到\0,它的返回值就......
  • 【c/c++】isdigit()函数
    isdigit函数isdigit是计算机C(C++)语言中的一个函数,主要用于检查其参数是否为十进制数字字符。函数定义:​​intisdigit(intc)​​​函数说明:​​检查参数是否为十进制......
  • #yyds干货盘点#再聊JS函数柯里化
    一、定义:柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。通过一个简单的例子......
  • Jmeter-重要的函数
     ${__counter(,)}计数器-加1的功能疑问:假如加2,使用计数器    -计数器超过最大值后重新开始计数重点:最大值,如果运行结果超过最大值时,又会从起始值开始循环每个......
  • 字符串分割函数
    1intsplitstr(conststd::string&str,chartag)2{3vector<string>vec_str;4std::stringsubStr;5for(size_ti=0;i<str.length();......
  • Java如何向主函数main中传入参数
    方法一:命令行java-jarWhite-1.0-SNAPSHOT.jar123方法二:javacGreenLeaf.javajavaGreenLeaf123方法三:IDEA->Run->EditConfigurations->Configuration->......
  • JDK8 四大核心函数式接口及扩展接口总结
    前言 Java8的四大函数式接口及相关的扩展接口在日常使用中的频率也是非常多的,包括自己定义的函数式接口,在JDK1.8之前,我们定义的方法都是用来接收参数,然后自己根据参数传......
  • strcpy函数
    strcpy,即stringcopy(字符串复制)的缩写。strcpy是C++/C语言语言的一个标准函数  ,strcpy把含有'\0'结束符(停止拷贝的终止条件)的字符串复制到另一个地址空间,返回值的类型为......
  • JavaScript函数详解:匿名函数、具名函数、函数传参、不定参、返回值、JS预解析机制
     JavaScript函数详解:匿名函数、具名函数、函数传参、不定参、返回值、JS预解析机制  1.具名函数 定义: 调用:方式1:方法名();可以多次调用  ......