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" <<x<< endl; }
};
class B :public A {
public:
    int y;
    void SetY(int a) { y = a; }
    B(int a = 0, int b = 0):A(a) { y = b; cout << "调用B构造函数" << endl;
    }
    void PB() { cout << "PB" << "x="<<x<<"y="<<y<<endl; }
};
class C :public A {
public:
    int z;
    void SetZ(int a) { z= a; }
    C(int a = 0, int b = 0) :A(a) { z = b; cout << "调用C构造函数" << endl; }
    void PC() { cout << "PC" <<"x="<<x<<"z="<<z<<endl; }
};
class D :public B, C {
    int m;
public:
    D(int a = 0, int b = 0, int c = 0,int d=0,int e=0):B(a,b),C(c,d) {  m = e; cout << "调用D构造函数" << endl;
    }
    void PD() {  PB();PC();cout<< "x=" <<x << "y=" << y << "z=" << z<< endl;
    }
};

已上代码会产生一个错误,在类D中无法识别数据成员x是哪一个。可以用限定符来限定,如下:

void PD() {  PB();PC();cout<< "x=" << B::x << "y=" << y << "z=" << z<< endl;

在这里插入图片描述

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

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

格式:


class ClassName:virtual <access> ClassName
{...};
或
class ClassName: <access> 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" <<x<< endl; }
};
class B :virtual public A {
public:
    int y;
    void SetY(int a) { y = a; }
    B(int a = 0, int b = 0):A(a)
     { y = b; cout << "调用B构造函数" << endl;  }
    void PB() { cout << "PB" << "x="<<x<<"y="<<y<<endl; }
};
class C :virtual public A {
public:
    int z;
    void SetZ(int a) { z= a; }
    C(int a = 0, int b = 0) :A(a) 
    { z = b; cout << "调用C构造函数" << endl; }
    void PC() { cout << "PC" <<"x="<<x<<"z="<<z<<endl; }
};
class D :public B, C {
    int m;
public:
    D(int a = 0, int b = 0, int c = 0,int d=0,int e=0):B(a,b),C(c,d) 
    {  m = e; cout << "调用D构造函数" << endl;
    }
    void PD() {  PB();PC();cout<< "x=" << x << "y=" << y << "z=" << z<< endl;
    }
};

主函数:

int main()
{
    D d1(1,2,3,4,5);
    d1.PD();
    return 0;
}

结果为:

在这里插入图片描述

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

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

虚函数

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

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

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

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

#include <iostream>
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"<<x<< endl; }
};

class B : public A {
public:
	int y;
void SetY(int a) 
	{ y = a; }
B(int a = 0, int b = 0):A(a) { y = b; cout << "调用B构造函数" << endl;
}
void PA() { cout <<"PB"  << " x= "<<x<<" y= "<<y<<endl; }
};

int main()
{
A a1(3), *a;//L1
a1.PA(); //L2
B b1(1,2); //L3
b1.PA(); //L4
a = &a1;
a->PA(); //L5
a = &b1;
a->PA(); //l6
return 0;
}

调试结果:
在这里插入图片描述

首先是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" <<x<< endl; }
     ~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 PB() { cout << "PB" << "x="<<x<<"y="<<y<<endl; }
};

调试结果如下:
在这里插入图片描述

也可以将主程序更改下:

delete (B*)a1;

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

纯虚函数

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

格式:

virtual 函数类型 函数名(参数表)=0
virtual <type>  FuncName(<ArgList>)=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="<<x<<"y="<<y<<endl; }
};
void A::print()
{
    cout << "PA" << x << endl;
}
主函数如下:

exit: Ctrl + ↩
int main()
{
    // A a1=new A;  出错,因为A有纯虚函数 不能实例化对象
    A *a1 = new B;
    a1->print();//调用的是派生类中的纯虚函数
    a1->A::print();//调用了A中的纯虚函数
    delete (B*)a1;
    return 0;
}

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值