1、什么是虚函数
virtual
修饰的成员函数就是虚函数
#include<iostream>
class MM
{
public:
virtual void print();
};
void MM::print()
{
cout << "我是虚函数" << endl;
}
以上定义了一个虚函数。我们需要提前知道一个空的类占用的是一个字节,在c语言中,不允许有空的结构体,但在c++中,允许有空的类。
class Boy
{
}
在实际中占用一个字节,因为标识位需要一个字节。而如果一个类里面有一个虚函数,那么其内存会增加4。如果有两个虚函数,那么其内存大小和这个类只有一个虚函数的大小一模一样。
因为这个4的本质内容是虚函数表指针
其指向虚函数表
,这个表里存放的全是指针。此处推荐这篇文章以供读者参考 https://blog.csdn.net/weixin_44302602/article/details/115227920
如果有虚函数,那么一定会有虚函数表,所有虚函数都是共用的一个虚函数表指针。
主函数如下
int main()
{
MM mm; //实例化一个对象
auto p = &MM::print;
//定义了函数指针
typedef void(MM::*Func)();
//这里定义了一个成员函数指针
//就是把函数类型定义出来,就是换了个别名
int** p = (int**)&mm;
//指针类型强制转换
//此处为什么是二级指针,因为虚函数表按照内存看,其是一个二维数组
Func pf = (Func)pp[0][0];
pf(); //输出为我是虚函数
return 0;
}
在实际中,并不会这样调用虚函数。虚函数被继承下来后,其仍然是虚函数。
什么是纯虚函数
纯虚函数就是没有函数体的虚函数(虚函数=0),其最大的作用为用来构造纯虚函数,至少一个纯虚函数的类叫做抽象类。抽象类无法构造对象。
如:
class MM
{
public:
virtual void print() = 0;
}
虚函数和多态
多态:
同一种行为的不同结果。(因为初始化的对象不同导致的行为不同),多态的必要关系有三点:
1.必须是公有继承
2.必须存在virtual
类型
3.必须存在不正常的指针引用
多态需要理解的是在继承中,同名对象的访问问题
class MM
{
public:
virtual void print()
{
cout << "MM::print" << endl;
}
protected:
};
class Boy: public MM
{
public:
void print()
{
cout << "Boy::print" << endl;
}
protected:
};
int main()
{
MM* pMM = new MM;
pMM->print();//输出为MM::print
Boy* pBoy = new Boy;
pBoy->print();//输出为Boy::print
//子类的方法将父类的方法覆盖掉,覆盖率父类的同名方法
//除非:
pBoy->MM:print();
return 0;
}
以上是正常的指针对象引用,即父类指针指向子类对象
MM* pMM;
pMM = new Boy();
pMM->print(); //输出为Boy:print
pMM = new MM();
pMM->print();//输出为MM::print
如果子类指针用父类初始化,其不够安全,方法如下:
pBoy = dynamic_cast<Boy*>(new MM);
if(pBoy != nullptr) pBoy->print();
抽象数据类型和析构函数
对于抽象数据类型,下面是一个栈就是一个抽象数据类型。这段代码有问题,将会在下面指出。
class Stack
{
public:
~Stack(){cout << "Stack delete" << endl;};
virtual void push(int data) = 0;
virtual void pop() = 0;
virtual int top() const = 0;
virtual bool empty() const = 0;
virtual int size() const = 0;
};
下面用数组实现栈:
class arrayStack: public Stack
{
public:
~arrayStack(){cout << "arrayStack delete";}
};
如果一个类继承了抽象类,子类没有完全重写父类的纯虚函数,那么其依然是一个抽象类。
当存在一种不正常的赋值时,会出现释放问题:
Stack* pstack = new arrayStack;
delete pstack;
//输出为Stack delete
此时并没有删除arrayStack
,因此此时需要虚要虚析构函数。构造函数没有虚构造函数,但是有虚析构函数
dynamic_cast
转化
主要可以进行上行转换、下行转换和交叉交换。
class MM
{
public:
MM(string name = "父类"):name (name){}
virtual void print() { cout << name << endl;}
string name;
};
class Son : public MM
{
public:
Son(string name = "子类"): sname(name){}
void print(){cout << sname << endl;}
void printData(){cout << sname << endl;}
string name;
};
测试代码如下:
/*上行转换*/
MM* upStatic = static_cast<MM*>(new Son);
upStatic->print();//输出为子类
MM* upDynamic = dynamic_cast<MM*>(new Son);
upDynamic->print();//输出为子类
/*下行转换*/
Son* downStatic = static_cast<Son*>(new MM);
downStatic->print();
downStatic->printData();//会报错
交叉转换是以父类为桥梁,实现子类与子类之间的转换。
class A
{
public:
virtual void print() { cout << "A" << endl;}
};
class B
{
public:
virtual void print() { cout << "B" << endl;}
};
class C: public A, public B
{
public:
void print(){cout << "C" << endl;}
}
测试代码
A* a = new C;
B* b = dynamic_cast<B*>(a);
b->print();//输出为C
return 0;
类中类的访问
class A
{
public:
class B
{
public:
void print();
}
}
访问时应该这样做:类似于层层剥洋葱的方法:
A::B b;
b.print();