多态 - C语言实现虚函数表(C语言实现C++的特性)

一.多态的理解

1.定义

定义:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。
条件:1. 必须通过基类的指针或者引用调用虚函数 2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

原因解释:多态是在继承的基础上实现的,子类(派生类)和父类(基类)之间满足切片原则,即可以通过父类(基类)的指针或者引用去调用虚函数

class Person
{
public:
    virtual void BuyTicket()
    {
        cout << "需要买成人票" << endl;
    }
};

class Student : public Person
{
public:
// 父类中+virtual,子类中可以不加(为析构开了个通道)
    void BuyTicket()
    {
        cout << "需要买学生票" << endl;
    }
};

void func(Person& p)
{
	p.BuyTicket();
}

2.重写 / 重载 / 覆盖

构成多态的第二个条件就是要构成虚函数的重写,虚函数重写的含义是在继承的基础上在父类上加virtual,在父类上加virtual可以不在子类上加virtual,因为继承后的析构函数也要写成虚函数的形式,仅在父类上加virtual就可以完成析构函数的重写

概念区分:1.重载:函数重载的必要条件就是必须要在同一个作用域,函数重载的实现使得程序中可以出现多个函数名相同函数,其中要求两个 / 多个函数的函数名 / 参数(参数指数量,参数类型和顺序可以不同(面试常考: 返回值不同不构成函数重载!!!)— 例: int func() / void func()不构成函数重载

条件:同一作用域,函数名 / 参数相同

重载是在同一作用域下,函数名相同情况下,通过改变函数的参数来实现的,参数的数量 / 参数的类型 / 相同参数下变量的类型顺序

2.函数重写(覆盖):函数重写是多态的重要条件,virtual是关键词,如果不是按照父类调用子类的条件,那么父类中的函数回直接将子类的函数覆盖,用子类 / 父类调用都是只是父类的函数

条件:两个函数分别在父类和子类的作用域,函数名 / 参数 / 返回值都必须相同 (协变例外)/ 两个函数都必须是虚函数(仅父类加virtual即可)

3.重定义(隐藏):重写和重定义类似,但是两个函数不在同一个作用域,所以不构成重写,重定义存在的意义也是使得继承类中存在同名的函数,但是重定义的条件较少,仅要求函数名相同即可(不需要管参数和返回值)

条件:两个函数分别在父类和子类的作用域, 函数名相同, 当两个在父类和子类的同名函数,不构成重写就是重定义

3.虚函数重写的两个例外

1.协变

派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。

class A{};
class B : public A 
{};
class Person 
{
public:
	virtual A* f() 
	{
		return new A;
	}
};

class Student : public Person 
{
public:
	virtual B* f() 
	{
		return new B;
	}
}

2.析构函数的重写

前面说到,虚函数只需要将父类上加virtual即可,子类可以不加virtual,就是为析构函数开了一个通路

如果父类的析构函数为虚函数,此时子类析构函数只要定义,无论是否加virtual关键字,都与父类的析构函数构成重写,虽然父类与子类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,为了防止析构函数无法构成重写,这里进行了特殊处理,仅在父类加virtual即可

class Person 
{
public:
	virtual ~Person() 
	{
		cout << "~Person()" << endl;
	}
};
class Student : public Person 
{
public:
	virtual ~Student() 
	{ 
		cout << "~Student()" << endl; 
	}
};
// 只有派生类Student的析构函数重写了Person的析构函数
// 下面的delete对象调用析构函数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
// 继承中只有将析构函数重写,才能正常完成调用
int main()
{
	Person* p1 = new Person;
	Person* p2 = new Student;
	
	delete p1;
	delete p2;
	
	return 0;
}

4.虚函数两个关键字

1.final:修饰虚函数,表示该虚函数不能再被重写

class A
{
public:
    virtual void func() final
    {};
};

class B : public A
{
    void func()   // Error
    {};
};

2.override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错

class A
{
public:
    virtual void func()
    {};
};

class B : public A
{
    void func() override
    {};
};

5.抽象类

。。。未完

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值