C++程序设计POJ》《WEEK6 多态与虚函数》
问题: 虚函数都是同名 同参数表的吗?
虚函数和普通成员函数的区别
虚函数可以多态,其他不行
在构造函数和析构函数中调用 虚函数 不是多态
派生类还没有初始化好
MyCompare()
qsort 怎么表示排序关系
虚函数表地址!!
虚函数
在类的定义中,前面有 virtual 关键字的成员
函数就是虚函数。
class base{
virtual int get() };
int base::get()
{ }
virtual 关键字只用在类定义里的函数声明中,
写函数体时不用。
多态的表现形式一
派生类的指针可以赋给基类指针。
通过基类指针调用基类和派生类中的同名 虚函数 时
1 )若该指针指向一个基类的对象,那么被调用
是基类的 虚函数
2 )若该指针指向一个派生类的对象,那么被调
用的是派生类的 虚函数 。
这种机制就叫做“
多态 ”。
class CBase { public: virtual void SomeVirtualFunction() { } }; class CDerived:public CBase { public : virtual void SomeVirtualFunction() { } }; int main() { CDerived ODerived; CBase * p = & ODerived; p-> SomeVirtualFunction(); // 调用哪个虚函数取决于 p 指向哪种类型的对象 调用CDerived类的虚函数 return 0; }
多态的表现形式二
派生类的对象可以赋给基类引用
通过基类引用调用基类和派生类中的同名 虚函数 时
1 )若该引用引用的是一个基类的对象,那么被
调用是基类的 虚函数
2 )若该引用引用的是一个派生类的对象,那么
被调用的是派生类的 虚函数 。
这种机制也叫做“
多态 ”。
class CBase { public: virtual void SomeVirtualFunction() { } }; class CDerived:public CBase { public : virtual void SomeVirtualFunction() { } }; int main() { CDerived ODerived; CBase & r = ODerived; r.SomeVirtualFunction(); //调用哪个虚函数取决于 r 引用哪种类型的对象 return 0; }
多态的作用
在面向对象的程序设计中使用多态,能够增强
程序的 可扩充性 ,即程序需要修改或增加功能
的时候,需要改动和增加的代码较少。
/* 几何形体处理程序 Sample Input: 3 R 3 5 C 9 T 3 4 5 Sample Output Triangle:6 Rectangle:15 Circle:254.34 39 */ #include<iostream> #include<stdlib.h> #include<math.h> using namespace std; class CShape { public: virtual double Area() = 0; // 纯虚函数 virtual void PrintInfo() = 0; }; class CRectangle :public CShape { public: int w, h; virtual double Area(); virtual void PrintInfo(); }; class CCircle :public CShape { public: int r; virtual double Area(); virtual void PrintInfo(); }; class CTriangle :public CShape { public: int a, b, c; virtual double Area(); virtual void PrintInfo(); }; double CRectangle::Area() { return w*h; } void CRectangle::PrintInfo() { cout << "rectanlgle:" << Area() << endl; } double CCircle::Area() { return 3.14*r*r; } void CCircle::PrintInfo() { cout << "circle:" << Area() << endl; } double CTriangle::Area() { double p = (a + b + c) / 2.0; return sqrt(p*(p - a)*(p - b)*(p - c)); } void CTriangle::PrintInfo() { cout << "triangle:" << Area() << endl; } CShape * pShapes[100]; // 基类 指针数组 int MyCompare(const void *s1, const void *s2); int MyCompare(const void* s1, const void *s2) { double a1, a2; CShape** p1; // s1,s2 是 void * ,不可写 ““* s1 ”来取得 s1 指向的内容 CShape** p2; p1 = (CShape**)s1;//s1,s2 指向 pShapes 数组中的元素,数组元素的类型是 CShape * p2 = (CShape**)s2;// 故 p1,p2 都是指向指针的指针,类型为 CShape ** a1 = (*p1)->Area();// * p1 的类型是 Cshape * , 是基类指针,故此句为多态 a2 = (*p2)->Area(); if (a1 < a2) return -1; else if (a2 < a1) return 1; else return 0; } int main() { int i, n; CRectangle *pr; CCircle *pc; CTriangle *pt; cin >> n; for (i = 0; i < n; i++) { char c; cin >> c; switch (c) { case 'R': pr = new CRectangle(); cin >> pr->w >> pr->h; pShapes[i] = pr; // 派生类指针 赋给 基类 break; case 'C': pc = new CCircle; cin >> pc->r; pShapes[i] = pc; break; case 'T': pt = new CTriangle(); cin >> pt->a >> pt->b >> pt->c; pShapes[i] = pt; break; } } qsort(pShapes, n, sizeof(CShape*), MyCompare); for (i = 0; i < n; i++) pShapes[i]->PrintInfo(); while (1); return 0; }
用基类指针数组存放指向各种派生类对象的指
针,然后遍历该数组,就能对各个派生类对象
做各种操作,是很常用的做法
构造函数和析构函数中调用虚函数
在构造函数和析构函数中调用虚函数,不是多态。编
译时即可确定,调用的函数是 自己的类或基类 中定义
的函数,不会等到运行时才决定调用自己的还是派生
类的函数。
派生类中和基类中虚函数同名同参数表的函数,不加
virtual 也自动成为虚函数
#include <iostream> using namespace std; class myclass { public: virtual void hello() { cout << "hello from myclass" << endl; }; virtual void bye() { cout << "bye from myclass" << endl; } }; class son :public myclass { public: void hello() { cout << "hello from son" << endl; } son() { hello(); } ~son() { bye(); } }; class grandson :public son { public: void hello() { cout << "hello from grandson" << endl; } void bye() { cout << "bye from grandson" << endl; } grandson() { cout << "constructing grandson" << endl; } ~grandson() { cout << "destructing grandson" << endl; } }; int main() { grandson gson; son *pson; // 不会调用构造函数?因为指针不需要分配内存空间? pson = &gson; pson->hello(); //多态 return 0; } //派生类中和基类中虚函数同名同参数表的函数,不加virtual 也自动成为虚函数 /* 结果: hello from son constructing grandson hello from grandson destructing grandson bye from myclass */
虚函数的访问权限
class Base { private: virtual void fun2() { cout << "Base::fun2()" << endl; } }; class Derived:public Base { public: virtual void fun2() { cout << "Derived:fun2()" << endl; } }; Derived d; Base * pBase = & d; pBase-> fun2(); // 编译出错
编译出错是因为 fun2() 是 Base 的私有成员。即使运行到此时实际上调用的应该是
Derived 的公有成员 fun2() 也不行,因为语法检查是不考虑运行结果的。
如果 将 Base 中的 private 换成 public, 即使 Derived 中的 fun2() 是 private 的,编译依然能通
过,也能正确调用 Derived::fun2() 。
#include<iostream> using namespace std; class Base { public: virtual void fun2() { cout << "base::fun2()" << endl; } }; class Derived :public Base { private: virtual void fun2() { cout << "Derived:fun2()" << endl; } }; int main() { Derived d; Base * pBase = &d; pBase->fun2(); // 神奇了,private也能访问!!! while (1); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix