9. C++虚函数与多态
虚函数
virtual修饰的成员函数就是虚函数
- 虚函数对类的内存影响:需要增加一个指针类型的内存大小
- 无论多少虚函数,只会增加一个指针类型的内存大小
- 虚函数表的概念: 指向虚函数的指针
我们自己也可以通过虚函数表指针去访问函数(一般做这样的操作不写数据类型)
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
virtual void print() //1.会写
{
cout << "第一个虚函数" << endl;
}
virtual void printData()
{
cout << "第二个虚函数" << endl;
}
protected:
};
int main()
{
cout << sizeof(MM) << endl; //2.对类内存影响
MM mm;
mm.print();
mm.printData();
//了解一下.32位没问题,64位 vs2022 问题
int** pObject = (int **)(&mm);
typedef void(*PF)();
PF pf = (PF)pObject[0][0];
pf(); //调用第一个虚函数
pf = (PF)pObject[0][1];
pf(); //调用第二个虚函数
return 0;
}
纯虚函数
具有一个或者多个纯虚函数的类型称之为抽象类,抽象类特性:
- 抽象类不能创建对象
- 抽象类可以创建对象指针
纯虚函数也是一个虚函数,所以也需要virtual修饰,纯虚函数是没有函数体,函数=0;
#include <iostream>
using namespace std;
//抽象类
class MM
{
public:
//纯虚函数
virtual void print() = 0;
protected:
string name;
};
int main()
{
//MM object; 抽象类不能构建对象
MM* pMM = nullptr;
return 0;
}
虚析构函数
virtual修饰的析构函数 就是虚析构函数
- 当父类指针被子类对象初始化的时候需要用虚析构函数
- 所有析构函数底层解析其实函数名相同
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
void print()
{
cout << "MM::print" << endl;
}
virtual ~MM() //虚析构函数
{
cout << "~MM" << endl;
}
};
class Son :public MM
{
public:
void print()
{
cout << "Son::print" << endl;
}
~Son()
{
cout << "~Son" << endl;
}
};
int main()
{
MM* pMM = new Son; //构造子类对象,必须构造父类对象在构造自身
pMM->print(); //MM看类型
delete pMM;
pMM = nullptr;
return 0;
}
虚函数和多态
多态的概念并不重要,重要的是需要知道那个对象指针在特定情况调用那个成员才是重要
多态概念: 指在继承中指针的同一行为的不同结果,举个栗子(男生和女生上厕所,都是上厕所的行为,男生站着,女生蹲着)
实现多态的两个前提条件:
- 必须是public继承
- 必须父类存在virtual类型的成员函数,并且子类中存在该函数的同名函数
- 一定存在指针的引用
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
void print()
{
cout << "MM::print" << endl;
}
virtual void printData()
{
cout << "MM virtual printData" << endl;
}
virtual ~MM() //虚析构函数
{
cout << "~MM" << endl;
}
};
class Son :public MM
{
public:
void print()
{
cout << "Son::print" << endl;
}
void printData()
{
cout << "Son printData" << endl;
}
~Son()
{
cout << "~Son" << endl;
}
};
int main()
{
//正常对象的访问,不存在多态
//都是就近原则
cout << "正常对象访问" << endl;
MM mmobject;
mmobject.print();
mmobject.printData();
Son sonobject;
sonobject.print();
sonobject.printData();
//正常的指针访问
cout << "正常指针访问" << endl;
MM* pMM = new MM;
pMM->print();
pMM->printData();
Son* pSon = new Son;
pSon->print();
pSon->printData();
//非正常的初始化
//父类指针被子类初始化
cout << "不正常的指针赋值" << endl;
MM* pObject = new Son;
pObject->print(); //没有virutal 看指针类型 调用MM::print
pObject->printData(); //有virtual 看对象 调用Son::printData
pObject = new MM;
pObject->printData(); //调用MM中
cout << "引用类型" << endl;
MM& girl = sonobject;
girl.print();
girl.printData();
return 0;
}
虚函数在继承特殊现象
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
virtual void print()
{
cout << "A" << endl;
}
virtual void printData() final //禁止子类重写方法
{
cout << "A" << endl;
}
};
//final: 父类中用来禁止子类重写同名方法
//override: 强制重写,起说明作用,表示当前子类当前方法是重写父类
class B :public A
{
public:
//重写:子类实现父类虚函数的同名函数
void print() override
{
cout << "B" << endl;
}
//void printData(){} //final禁止重写
};
class C :public B
{
public:
void print()
{
cout << "C" << endl;
}
};
int main()
{
B* pb = new C;
pb->print(); //调用C::print
pb = new B;
pb->print(); //调用B::print
return 0;
}
纯虚函数和ADT
ADT: 抽象数据类型
抽象类本身不能创建对象,但是子类如果重写类父类中纯虚函数,子类是可以被允许创建对象
抽象类一般用于架构项目,构建好整个项目模块,具体细致工作可以交给子类去实现
采用ADT方式设计项目,可以把这个模块构建出来,并且测试代码也可以提前完成。
#include <iostream>
using namespace std;
//抽象产品类
class AbstractProduct
{
public:
virtual void printProduct() = 0;
};
//抽象系统类---ADT
//析构函数一定写虚析构函数
class AbstractSystem
{
public:
~AbstractSystem() {}
virtual void insertData(AbstractProduct* product) = 0;
virtual void printData()const = 0;
virtual int size() const = 0;
virtual bool empty() const = 0;
};
class ArraySystem :public AbstractSystem
{
public:
void insertData(AbstractProduct* product)
{
}
void printData()const
{
}
int size() const
{
return 0;
}
bool empty() const
{
return 0;
}
};
class ListSystem :public AbstractSystem
{
public:
void insertData(AbstractProduct* product)
{
}
void printData()const
{
}
int size() const
{
return 0;
}
bool empty() const
{
return 0;
}
};
int main()
{
AbstractSystem* p =new ArraySystem;
p->printData();
//UI中用的比较多
//MFC --->不需要自己创建,只需要重写一个,构建对象即可
p = new ListSystem;
p->printData();
return 0;
}