C++继承&虚函数

Part 1. 虚函数
关于C++继承,我们先来看一小段代码。Base表示基类,Derived表示Base的继承类。

#include<iostream>
using namespace std;
class Base{//基类
public:
    Base(int a, int b):base_a(a),base_b(b) {};
    ~Base() { cout << "Base destructor!" << endl; };

    void print(){
        cout << base_a << " " << base_b << endl;
    }
protected://protected权限,继承类可访问
    int base_a, base_b;
};

class Derived : public Base{//继承类
public:
    Derived(int a, int b, int c):Base(a, b), derived_c(c) {};
    ~Derived() { cout << "Derived destructor!" << endl; };

    void print(){
        cout << base_a << " " << base_b <<" "<< derived_c << endl;
    }
private:
    int derived_c;
};
int main(){
    Derived *d = new Derived(1, 2, 3);//继承类的指针操作继承类的成员
    d->print();//调用继承类函数
    delete d;//先释放继承类的资源,再释放基类的资源

    Base *p = new Derived(1, 2, 3);//基类指针指向继承类
    p->print();//调用基类函数
    delete p;//只释放基类资源,未释放继承类资源

    return 0;
}

运行结果为,
这里写图片描述

注意到,

Base *p = new Derived(1, 2, 3);//基类指针指向继承类
p->print();//调用基类函数
delete p;//只释放基类资源,未释放继承类资源

即便基类指针指向的是继承类的对象,但实际调用的是仍然是基类的成员函数和析构函数,特别地,在析构函数调用时,继承类析构函数未被调用,这样易造成内存泄漏!这是因为,当基类指针被用来指向继承类对象时,会进行指针类型转换,将继承类对象的指针先转换为基类的指针,所以基类指针指向的是继承类对象中的基类部分,这种方法是无法通过基类指针去调用派生类对象中的成员函数的。

解决方法是,通过使用虚函数:当把基类的某个成员函数声明为虚函数后,允许在其继承类中对该函数进行重新定义,赋予它新的功能,并且可以通过指向基类的指针指向不同的继承类,从而调用其中的同名函数。这样看来,虚函数可以实现动态调用的效果。

由此,我们调整代码,

virtual ~Base() { cout << "Base destructor!" << endl; };

virtual void print(){
    cout << base_a << " " << base_b << endl;
}

运行结果为,
这里写图片描述

可以看出,当基类指针指向继承类的对象时,如果使用虚函数,可以使得调用的函数为继承类中重新定义的方法。当基类有多个继承类时,可以通过这种方法方便地实现不同继承类方法的动态绑定!

part 2. 纯虚函数
纯虚函数没有实现体,基本形式为, virtual void function_name()=0; 如,

class Figure{
public:
    ...
    virtual void draw() = 0; 
    virtual void rotate(double) = 0;
    ...           
};

此时,Figure类就变成了抽象类,抽象类不能实例化一个对象,但可以有指针,具体的实现由其继承类来实现。当基类方法不好确定或将来的行为多种多样时,而此行为又是必须的,就可以在基类的设计中,引入虚函数的概念。

这里列举一个完整的例子,

#include <math.h>
#include <iostream>
using namespace std;
class Point
{
private:
    double x;
    double y;
public:
    Point(double i, double j) : x(i), y(j) { }
    void print() const{ cout << "(" << x << ", " << y << ")"; }
};

class Figure//基类
{
private:
    Point center;
public:
    Figure(double i = 0, double j = 0) : center(i, j) { }

    //注意:这里函数是返回引用,而不是副本(非引用);但如果返回值是函数内部变量时,则无法使用返回引用,否则当函数调用完成时,临时变量也会被撤销,程序报错。
    Point& location(){ return center;}     

    void move(Point p)
    {
        center = p;
        draw();
    }
    //虚函数
    virtual void draw() = 0; 
    virtual void rotate(double) = 0;               
};

class Circle : public Figure//Figure 的继承类
{
private:
    double radius;
public:
    Circle(double i = 0, double j = 0, double r = 0) : Figure(i, j), radius(r) { }

    void draw()
    {
        cout << "A circle with center ";
        location().print();
        cout << " and radius " << radius << endl;
    }

    void rotate(double){ cout << "no effect.\n";}       
};

class Square : public Figure//Figure的另一个继承类
{
private:
    double side;        
    double angle;        
public:
    Square(double i = 0, double j = 0, double d = 0, double a = 0) : Figure(i, j), side(d), angle(a) { }
    void draw()
    {
        cout << "A square with center ";
        location().print();
        cout << " side length " << side << ".\n"
            << "The angle between one side and the X-axis is " << angle << endl;
    }

    void rotate(double a)
    {
        angle += a;
        cout << "The angle between one side and the X-axis is " << angle << endl;
    }

    void vertices()
    {
        cout << "The vertices of the square are:\n";
    }
};
int main()
{
    Circle c(1, 2, 3);
    Square s(4, 5, 6);
    Figure *f = &c, &g = s;

    f->draw();
    f->move(Point(2, 2));
    g.draw();
    g.rotate(1);

    s.vertices();//这里无法使用指针类型g调用方法vertices,这是因为该方法是继承类中所拥有的函数
    return 0;
}

运行结果为,
这里写图片描述

基类Figure类中的虚函数draw()和rotate(double)方法没有方法体,具体的实现是在继承类Square类和Circle类中实现的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值