面向对象程序设计(3)虚函数、重载重写覆盖、泛型、多态、override、final

目录

1,虚函数

2,重载、重写、覆盖

3,泛型程序设计

4,多态

5,虚析构函数

6,判断函数是否是虚函数、override

7,final


1,虚函数

如果类中包含虚函数(virtual),那么对象的首地址就是一个虚函数表,表中是一系列虚函数指针,每一个指向一个虚函数。

如果基类的函数被定义为虚函数(virtual),那么派生类中重定义的函数也都自动是虚函数,不用加virtual

虚函数允许用基类类型的指针,调用子类的这个函数。

PS:如果基类中的不是虚函数,派生类中也可以定义为虚函数,不过一般估计不会这么做。

2,重载、重写、覆盖

函数重载overload,是多个函数名字相同,但签名不同,既适用于普通函数之间,也适用于同一个类的成员函数之间,无论是构造函数还是普通成员函数,但不用于析构函数。

函数重写override,也叫函数重定义,指的是在派生类中重定义基类的虚函数,重定义的函数和基类函数具有相同签名,无论是构造函数、析构函数还是普通成员函数,都可以重定义。

函数覆盖hide,指的是在派生类中重定义基类的非虚函数,重写可以实现多态,覆盖不能。

示例:

#include<iostream>
using namespace std;

class C
{
public:
    string toString()
    {
        return "class C";
    }
    string toString(string x)
    {
        return "class C"+x;
    }
};
class B:public C
{
public:
    int toString()
    {
        return 0;
    }
};
class A :public B
{
public:
    string toString()
    {
        return "class A";
    }
};

int main()
{
    string s="abc";
    cout << C().toString() << endl;
    cout << C().toString(s) << endl;//函数重载
    cout << B().toString() << endl;//函数重定义
    cout << A().toString() << endl;//函数重定义
    return 0;
}

输出结果:

class C
class Cabc
0
class A

如果函数重定义之后,派生类对象要调用基类函数,使用基类名和作用域解析运算符::

class B:public C
{
public:
    int toString()
    {
        cout<<C::toString()<<endl;
        return 0;
    }
};

如果父类中的是非虚函数,子类中加virtual关键字,那么结果仍然是覆盖,非多态。

3,泛型程序设计

C++的泛型设计有好几种设计,普通函数的重载、运算符重载、类成员函数重载和重定义、多态,都属于泛型程序设计的范畴。

在程序中需要一个基类对象时,向其提供一个派生类对象是允许的。

这种特性使得一个函数可适用于较大范围的对象实参,这也是一种泛型程序设计,而这已经非常接近多态的用法了。

#include<iostream>
using namespace std;

class C
{
public:
    string toString()
    {
        return "class C";
    }
};
class B:public C
{
public:
    string toString()
    {
        return "class B";
    }
};
class A :public B
{
public:
    string toString()
    {
        return "class A";
    }
};

void display(C x)
{
    cout << x.toString() << endl;
}

int main()
{
    display(A());//泛型程序设计
    display(B());
    display(C());
    return 0;
}

输出:

class C
class C
class C

4,多态

在上面的例子中,三次调用display函数,实际调用的都是C的toString函数。

要想实现每个对象调用自己类的函数,即实现多态,需要用虚函数和对象指针。

示例:

#include<iostream>
using namespace std;

class C
{
public:
    virtual string toString()
    {
        return "class C";
    }
};
class B:public C
{
public:
    string toString()
    {
        return "class B";
    }
};
class A :public B
{
public:
    string toString()
    {
        return "class A";
    }
};

void display(C *p)
{
    cout << p->toString() << endl;
}

int main()
{
    C* p= new A();
    display(p);
    p= new B();
    display(p);
    p= new C();
    display(p);
    return 0;
}

输出结果:

class A
class B
class C

用传引用的方式也可以实现多态:

void display(C& p)
{
    cout << p.toString() << endl;
}

int main()
{
    C* p= new A();
    display(*p);
    p= new B();
    display(*p);
    p= new C();
    display(*p);
    return 0;
}

用模板可以实现类似的功能:

template<typename T>
void display(T x)
{
    cout << x.toString() << endl;
}

int main()
{
    display(A());
    display(B());
    display(C());
    return 0;
}

输出是一样的。

模板是编译期多态,虚函数多态是运行时多态。

5,虚析构函数

正常析构过程:

#include<iostream>
using namespace std;

class Person
{
public:
    Person()
    {
        cout << "Person con" << endl;
    }
    ~Person()
    {
        cout << "Person des" << endl;
    }
};

class Employee:public Person
{
public:
    Employee()
    {
        cout << "Employee con" << endl;
    }
    ~Employee()
    {
        cout << "Employee des" << endl;
    }
};

class Fac :public Employee
{
public:
    Fac()
    {
        cout << "Fac con" << endl;
    }
    ~Fac()
    {
        cout << "Fac des" << endl;
    }
};

int main()
{
    auto* fac=new Fac;
    delete fac;
    return 0;
}

输出:

Person con
Employee con
Fac con
Fac des
Employee des
Person des

这里的fac类型其实就是子类Fac类型。

异常析构过程:

int main()
{
    Person* fac=new Fac;
    delete fac;
    return 0;
}

输出:

Person con
Employee con
Fac con
Person des

只调用了父类的析构函数。

这样很容易出错,毕竟把指向子类对象的指针类型设为父类类型是非常常用且重要的,

这种情况下为了避免new出来的对象delete不干净,可以把父类析构函数设为虚函数:

class Person
{
public:
    Person()
    {
        cout << "Person con" << endl;
    }
    virtual ~Person()
    {
        cout << "Person des" << endl;
    }
};

一般地,在不考虑极致性能的情况下,所有的析构函数都可以设为虚函数。

6,判断函数是否是虚函数、override

如果继承关系比较复杂,那么如何判断一个函数是不是虚函数呢?

方法一:利用结论 重写可以实现多态,覆盖不能

#include <iostream>
using namespace std;

class X
{
    //unknown
};

class A:public X
{
public:
    int f()
    {
        return 0;
    }
};

class B:public A
{
public:
    int f()
    {
        return 12345678;
    }
};

int main() {
    B b;
    A* p = &b;
    if(p->f()==b.f())cout<<"same, virtual function";
    else cout<<"not same, not virtual function";
    return 0;
}

方法二:利用关键字override或者final

只有虚函数可以用override修饰

#include<iostream>
using std::cout;

class X
{
    //unknown
};

class A:public X
{
public:
    int f() override 
    {
        return 0;
    }
};

int main()
{
    return 0;
}

只要能编译过就说明A::f是虚函数

同理,用final也可以

7,final

(1)修饰函数

只有虚函数可以用final修饰,表示函数不能被重写

class A
{
public:
    virtual int f() final
    {
        return 0;
    }
};

class B: public A
{
    int f()
    {
        return 0;
    }
};

报错:Declaration of 'f' overrides a 'final' function

(2)修饰类

表示类不能被继承

class A final
{
public:
    virtual int f()
    {
        return 0;
    }
};

class B: public A
{
};

报错:Base 'A' is marked 'final'

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值