c++虚函数

虚基类

意义:假设定义了一个公共基类A。类B和类C都由类A公有派生,类D由类B和类C公有派生。显然D包含类A的两个拷贝,不仅多占用内存,而且还造成多个拷贝的数据不一致。

不定义虚基类的效果如下:

class A {

public:
    int x;
    void SetX(int a) { x = a; }
    A(int a = 0) { x = a; cout << "调用A构造函数" << endl;
    }
    void PA() { cout << "PA" <c++虚函数_第1张图片

所以从调试结果可以看出 构造了两个类A的对象。

分析:若要早多重继承中,希望公共基类在派生类中只有一个拷贝,可将该基类说明为虚基类。即在定义派生类时,在继承的公共基类类名钱加上关键字virtual。

格式:


class ClassName:virtual  ClassName
{...};
或
class ClassName:  virtual ClassName
{...};

实例如下:

class A {

public:
    int x;
    void SetX(int a) { x = a; }
    A(int a = 0) 
    { x = a; cout << "调用A构造函数" << endl;
    }
    void PA() { cout << "PA" <c++虚函数_第2张图片

所以,当基类设置为虚基类时,只拷贝一次虚基类。
但是,为何基类的数据成员x数值为0???

因为类D的构造函数调用了B和C的构造函数,类B和C又调用了类A的构造函数,但是因为由于虚基类在D中只有一个拷贝,编译器无法确定是由B还是C的构造函数调用A的构造函数,所以,编译器约定,执行B和C的构造函数时不调用虚基类A的构造函数,而在类D的构造函数中直接调用A的默认的构造函数。
如果派生类继承了多个基类,基类中有虚基类和非虚基类,那么在创建该派生类的对象时,先调用虚基类的构造函数,然后调用非虚基类的构造函数,最后调用派生类的构造函数。若虚基类有多个,则虚基类构造函数的调用顺序取决于它们继承时的说明顺序。

虚函数

多态性是OOP的重要特性之一。利用多态性可以调用同名函数,实现不同功能。
多态性分为运行时多态性和编译时的多态性。
编译时的多态性通过函数重载或运算符重载实现,运算符重载本质上是函数重载。
运行时的多态性是指在程序执行前,根据函数名和参数无法确定调用哪个函数,它通过类的继承关系和虚函数来实现,主要用来建立实用的类层次体系结构。
定义:类中的非静态成员函数可以定义为虚函数。
格式: virtual FuncName();
特性:

继承性:若某类中有某个虚函数,则在其派生类中,该虚函数保持虚函数特性

可重定义: 若某类中有虚函数。在派生类中可重定义该虚函数,此时不用vritual修饰,仍保持虚函数特性,但为了提高程序可读性,一般加上vritual。

在派生类中重定义虚函数时,必须与基类的同名虚函数的参数个数,参数类型及返回值类型完全一致。

#include 
using namespace std;

class A {
public:
	int x;
void SetX(int a) { x = a; }
A(int a = 0) 
{
 	x = a; 
	cout<<"调用A构造函数"<< endl;
}
void virtual PA() { cout <<"PA"<c++虚函数_第3张图片

首先是L1行,定义一个A的对象,输出调用A构造函数,L2行输出PA3,L3行输出调用A构造函数,调用B构造函数。
然后L4行调用的是类B中的函数PA(),这是利用支配规则,默认调用派生类的函数,和虚函数无关,体现的是编译时的多态性
L6行,因为将派生类对象赋给基类的指针变量,符合赋值兼容规则,因为PA为基类中的虚函数并在派生类中重定义,所以调用派生类的虚函数PA()。体现运行时的多态性。

说明:

1.使用基类类型的指针变量或引用,使该指针指向派生类的对象或引用,并通过指针调用指针所指对象的虚函数才能实现运行时的多态性。
2. 不能将构造函数定义为虚函数,但通常把析构函数定义为虚函数,以便通过运行时的多态性,正确释放基类及派生类申请的动态内存
3. 虚函数比一般成员函数执行慢一些,因为为了实现运行时多态性,每个派生类中=均要保存相应虚函数的入口地址表。

虚析构函数

当派生类中动态申请内存空间时,而新建一个基类类型的指针变量,指向派生类空间,如下:

A *a1 = new B;
a1->PA();
delete a1;

则程序只会调用基类的析构函数而不会调用派生类的构造函数,所以无法释放动态内存。当将基类的析构函数设置为虚函数时,则会调用派生类的析构函数。如下:

class A {

public:
    char *name;
    int x;
    A(int a = 0) { x = a; cout << "调用A构造函数" << endl;
    }
    void virtual PA() { cout << "PA" <c++虚函数_第4张图片

也可以将主程序更改下:

delete (B*)a1;

此时执行结果和上面一致。

纯虚函数

意义:在定义基类时,只能抽象出虚函数的原型,而不定义其实现,其实现依赖于它的派生类,这时,可把基类中的虚函数定义为纯虚函数。
在基类中为其派生类保留一个函数名字

格式:

virtual 函数类型 函数名(参数表)=0
virtual   FuncName()=0;

说明: 1. 定义纯虚函数时,其实现不能在类内同时定义,但可在类外或其派生类中定义

虚函数赋值为0,并不表示函数返回值为零,起形式上的作用,纯虚函数不具备函数功能不能被调用。
含有纯虚函数的类肯定是抽象类,因为虚函数没有实现部分,不能产生对象。
但可定义抽象类类型的指针,以便用这种基类类型的指针变量指向其派生类的对象时,调用其派生类重定义的纯虚函数。引发运行时的多态性。

如下:

class A {

public:
    char *name;
    int x;
    A(int a = 0) { x = a; cout << "调用A构造函数" << endl;
    }
    void virtual print() = 0;
      ~A() { cout << "调用A的析构函数" << endl; }
};
class B : public A {
public:
    int y;
    B(int a = 0, int b = 0):A(a) { 
        name = new char[10];
        y = b; cout << "调用B构造函数" << endl;
    }
     ~B() 
    { 
        delete[] name;
        cout << "调用B的析构函数" << endl;
    }
    void print() { cout << "PB" << "x="<print();//调用的是派生类中的纯虚函数
    a1->A::print();//调用了A中的纯虚函数
    delete (B*)a1;
    return 0;
}

来源:CSDN
原文:https://blog.csdn.net/qq_21961385/article/details/82858430

你可能感兴趣的:(c++)