这里写目录标题
- 虚函数
- 虚函数实现动态绑定
- 虚继承
- 抽象类
虚函数
虚函数是在C 中用于实现多态性的一种特殊函数。它通过使用关键字"virtual"进行声明,在基类中定义,可在派生类中进行重写。虚函数允许在运行时根据对象的实际类型来调用相应的函数,而不仅仅根据指针或引用的类型。这使得在继承关系中,通过基类指针或引用调用虚函数时,可以根据实际对象的类型来动态地确定要执行的函数版本,实现多态性的特性。
虚函数定义
在 C 中,可以通过在函数声明前面加上关键字 virtual 来定义虚函数。例如:
代码语言:javascript复制class Base {
public:
virtual void virtualFunction() {
// 函数定义
}
};
在这个例子中,virtualFunction 被声明为虚函数。派生类可以重写这个虚函数,实现多态性。
虚函数实现动态绑定
动态绑定(Dynamic Binding),也称为运行时多态性(Runtime Polymorphism),是通过在基类和派生类中使用虚函数来实现的。
在C 中,当基类的指针或引用指向派生类的对象时,通过调用虚函数,可以实现对应于派生类的特定实现。这种根据对象的实际类型来确定调用哪个函数的机制就是动态绑定。
动态绑定的关键在于使用virtual关键字将成员函数声明为虚函数,并在基类和派生类中提供相应的实现。
代码语言:javascript复制#include<iostream>
#include <cstring>
using namespace std;
class cemployee
{
public:
int m_id;
char name[10];
cemployee()
{
memset(name,0,10);
}
virtual void outputname()
{
cout<<"employee name:"<<name<<endl;
}
};
class comployee:public cemployee
{
public:
char password[10];
void outputname()
{
cout<<"opertor name:"<<name<<endl;
}
};
int main()
{
cemployee* pworker = new comployee();
strcpy(pworker->name,"MP");
pworker->outputname();
delete pworker;
return 0;
}
动态多态满足关系: 1.有继承关系 2.子类重写父类的虚函数 动态多态使用:父类的指针或引用 指向子类对象 重写:函数返回值类型 函数名 参数列表 完全一致叫重写
如果子类中没有堆区数据,可以不用写虚析构和纯虚析构。
1.虚析构与纯虚析构共性: 解决父类指针释放子类对象不干净问题 都需要有具体的函数实现
2.区别: 如果是纯虚析构,该类属于抽象类,无法实例化 .虚析构语法: virtual ~类名(){} 纯虚析构语法: virtual ~类名()=0; 类名::~类名(){}
纯虚数 子类的内容会覆盖父类,所以父类中函数没有意义了 类中只要有一个纯虚函数就称为抽象类 virtual void func() = 0; 抽象类无法实例化对象(堆区,栈区) 子类也必须要重写父类中的虚函数,否则子类也就是抽象类
具体代码示意如下所示
代码语言:javascript复制#include<iostream>
using namespace std;
class animal
{
public:
void speak()
{
cout << "dongwuzaishouhua" << endl;
}
void speak()
{
cout << "dongwuzaishouhua" << endl;
}
class cat :public animal
{
public:
void speak()
{
cout << "xiaomaozaishuohua" << endl;
}
};
class dog :public animal
{
public:
virtual void speak()
{
cout << "xioagouzaishuoihua" << endl;
}
//注释之后对象模型:
class dog size(1):
---
0 | --- (base class animal)
| ---
---
};
void dospeak(animal& animal) //aniaml& aniaml= cat
{
animal.speak(); //会打印出dongwuzaishouhua,因为aniaml&
}
void test01()
{
cat cat;
dospeak(cat);
dog dog;
dospeak(dog);
}
/*
vfptr: 虚函数表指针
v- virtual
f- function
ptr- pointor
vftable:虚函数表
v- virtual
f- function
table- table
*/
![请添加图片描述](https://img-blog.csdnimg.cn/direct/0194baae48ad4111aa4acc0ce9f011df.png)
/* 有函数对象时模型:
class dog size(4):
---
0 | --- (base class animal)
0 | | {vfptr}
| ---
---
dog::$vftable@:
| &dog_meta
| 0
0 | &dog::speak //覆盖 父类的指针或引用 指向子类对象发生多态
*/
class base
{
public:
//纯虚数
// 子类的内容会覆盖父类,所以父类中函数没有意义了
//类中只要有一个纯虚函数就称为抽象类
virtual void func() = 0;
/*
抽象类无法实例化对象(堆区,栈区)
子类也必须要重写父类中的虚函数,否则子类也就是抽象类
*/
virtual ~base()
{
cout << "base的析构函数" << endl;
}
};
class son :public base
{
public:
virtual void func()
{
cout << "fff";
}
virtual ~son()
{
cout << "son的析构函数" << endl;
}
};
void test00()
{
//son s;不允许使用抽象类类型 "son" 的对象
//base s;
//new base;
/*base* b = new son;
b->func();*/
}
int main()
{
//test01();
test00();
system("pause");
return 0;
}
虚继承
虚继承(Virtual Inheritance)是C 中的一种继承方式,用于解决多继承中的菱形继承问题。
在多重继承中,如果一个派生类从两个或更多的基类继承,而这些基类又共同继承自同一个基类,就会出现菱形继承问题。这种情况下,派生类会包含同一个基类的多份拷贝,导致二义性和内存浪费。
虚继承通过使用virtual关键字修饰基类,在派生类对该基类进行继承时,确保只有一份共享的基类子对象被创建,从而解决了菱形继承问题。
代码语言:javascript复制#include<iostream>
using namespace std;
class cnaimal
{
public:
cnaimal(){
cout<<"animal was created"<<endl;
}
void move(){
cout<<"animal could moving"<<endl;
}
};
class cbird:public cnaimal
{
public:
cbird(){
cout<<"bird was created"<<endl;
}
void fly(){
cout<<"bird would flying"<<endl;
}
};
class cfish:public cnaimal
{
public:
cfish(){
cout<<"fish was created"<<endl;
}
void swim(){
cout<<"fish would swim"<<endl;
}
};
class cwaterbird: public cbird, public cfish
{
public:
cwaterbird(){
cout<<"cwaterbird was created"<<endl;
}
};
int main()
{
cwaterbird waterbird;
return 0;
}
运行结果·:
代码语言:javascript复制[bsk@localhost polymorphic]$ g virtualinheritance.cpp
[bsk@localhost polymorphic]$ ./a.out
animal was created
bird was created
animal was created
fish was created
cwaterbird was created
我们可以看到,当只创建一个cwaterbird类时,由于它继承于cbird类和cfish类,所以会先去调用他们的构造函数,但是他们又继承于canimal类,于是又先去调用animal was created,然后再是鸟类的自身构造,bird was created,鸟类构造完之后,又来构造鱼类,同鸟类一样,先是animal was created,再是自身构造,fish was created,最后才是cwaterbird was created
代码语言:javascript复制#include<iostream>
using namespace std;
class cnaimal
{
public:
cnaimal(){
cout<<"animal was created"<<endl;
}
void move(){
cout<<"animal could moving"<<endl;
}
};
class cbird:virtual public cnaimal
{
public:
cbird(){
cout<<"bird was created"<<endl;
}
void fly(){
cout<<"bird would flying"<<endl;
}
};
class cfish:virtual public cnaimal
{
public:
cfish(){
cout<<"fish was created"<<endl;
}
void swim(){
cout<<"fish would swim"<<endl;
}
};
class cwaterbird: public cbird, public cfish
{
public:
cwaterbird(){
cout<<"cwaterbird was created"<<endl;
}
};
int main()
{
cwaterbird waterbird;
return 0;
}
结果·如下所示:
代码语言:javascript复制[bsk@localhost polymorphic]$ ./a.out
animal was created
bird was created
fish was created
cwaterbird was created
可见,当类是虚继承时,我们可以发现animal类的构造就只有一个了,
抽象类
抽象类包含有纯虚函数的类,一个抽象类至少有一个纯虚函数。抽象类只能作为基类派生出的新子类,而不能在程序中被实例化(不能声明抽象类的对象),但是可以指向抽象类的指针。
纯虚函数(Pure Virtual Function)是在基类中声明的没有实际实现的虚函数。它通过在函数声明后面加上= 0来表示。
纯虚函数在基类中起到了接口的定义作用,要求派生类必须提供对应的实现。如果一个类包含了纯虚函数,那么它就成为了抽象类,无法被直接实例化,只能作为基类来派生其他类。
纯虚函数使用的语法如下:
代码语言:javascript复制class Base {
public:
virtual void pureVirtualFunction() = 0;
};
在上述示例中,Base类中的pureVirtualFunction函数被声明为纯虚函数。该函数没有实际的实现,只是作为接口的定义存在。
派生类必须提供对纯虚函数的实现,否则它们也会成为抽象类。一个派生类可以选择重写纯虚函数,也可以将其继续声明为纯虚函数,这取决于派生类是否需要进一步派生。
一个简单的示例:
代码语言:javascript复制#include <iostream>
class Base {
public:
virtual void pureVirtualFunction() = 0;
};
class Derived : public Base {
public:
void pureVirtualFunction() override {
std::cout << "Derived class implementation." << std::endl;
}
};
int main() {
// Base base; // 错误,无法实例化抽象类
Derived derived;
derived.pureVirtualFunction(); // 调用Derived类的实现
Base* basePtr = &derived;
basePtr->pureVirtualFunction(); // 通过基类指针调用Derived类的实现
return 0;
}
在上述示例中,Base类中的pureVirtualFunction函数被声明为纯虚函数。Derived类继承自Base类,并提供了对纯虚函数的具体实现。通过Derived类的对象或基类指针可以调用纯虚函数的具体实现。
纯虚函数允许在基类中定义一组接口,并强制要求派生类提供相应的实现。它是实现抽象类和多态性的重要机制之一。 如果某个函数不是抽象类中的成员函数,不能用基类指针调用。