通过基类指针只能访问派生类的成员变量,但是不能访问派生类的成员函数。
为了消除这种情况,让基类指针能够访问派生类的成员函数,C++ 增加了虚函数(Virtual Function)。使用虚函数,需要在函数声明前面增加 virtual 关键字。
#include <windows.h> #include "resource.h" #include <string> using namespace std; char szBuffer[100]; //基类People class People{ public: People(char *name, int age); virtual void display(HDC hdc); //声明为虚函数 protected: char *m_name; int m_age; }; People::People(char *name, int age): m_name(name), m_age(age){} void People::display(HDC hdc){ wsprintf(szBuffer, "%s今年%d岁了,是个无业游民。",m_name,m_age); TextOut(hdc,10,30,szBuffer,lstrlen(szBuffer)); } //派生类Teacher class Teacher: public People{ public: Teacher(char *name, int age, int salary); virtual void display(HDC hdc); //声明为虚函数 private: int m_salary; }; Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){} void Teacher::display(HDC hdc){ wsprintf(szBuffer, "%s今年%d岁了,是一名教师,每月有%d元的收入",m_name,m_age,m_salary); TextOut(hdc,10,80,szBuffer,lstrlen(szBuffer)); } LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); HINSTANCE hInst; TCHAR szClassName[] = TEXT("virtfDemo"); int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) { HWND hwnd; MSG messages; WNDCLASSEX wincl; hInst = hThisInstance; wincl.hInstance = hThisInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; wincl.style = CS_DBLCLKS; wincl.cbSize = sizeof (WNDCLASSEX); wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszMenuName = MAKEINTRESOURCE (IDC_VIRTFDEMO); wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; wincl.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); if (!RegisterClassEx (&wincl)) return 0; hwnd = CreateWindowEx ( 0, szClassName, TEXT("C++虚函数Demo"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 200, HWND_DESKTOP, NULL, hThisInstance, NULL ); ShowWindow (hwnd, nFunsterStil); while (GetMessage (&messages, NULL, 0, 0)) { TranslateMessage(&messages); DispatchMessage(&messages); } return messages.wParam; } LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; RECT rt; char szBuffer[100]; People *p = new People("王不二", 23); switch (message) { case WM_COMMAND: switch (LOWORD(wParam)) { case IDM_virtf: hdc=GetDC(hwnd); p -> display(hdc); p = new Teacher("赵杰杰", 45, 9000); p -> display(hdc); break; case IDM_ABOUT: MessageBox (hwnd, TEXT ("virtfDemo v1.0\nCopyright (C) 2020\n by bo"), TEXT ("virtfDemo"), MB_OK | MB_ICONINFORMATION); break; case IDM_EXIT: DestroyWindow(hwnd); break; default: return DefWindowProc(hwnd, message, wParam, lParam); } break; case WM_CREATE: break; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rt); EndPaint(hwnd, &ps); break; case WM_DESTROY: PostQuitMessage (0); break; default: return DefWindowProc (hwnd, message, wParam, lParam); } return 0; }例子中,可以通过 p 指针调用 Teacher 类的成员函数,运行结果也证明了这一点(赵杰杰已经是一名老师了,不再是无业游民)。
有了虚函数,基类指针指向基类对象时就使用基类的成员(包括成员函数和成员变量),指向派生类对象时就使用派生类的成员。换句话说,基类指针可以按照基类的方式来做事,也可以按照派生类的方式来做事,它有多种形态,或者说有多种表现方式,这就是 多态(Polymorphism)。
同样是p->display();这条语句,当 p 指向不同的对象时,它执行的操作是不一样的。同一条语句可以执行不同的操作,看起来有不同表现方式,这就是多态。
多态是面向对象编程的主要特征之一,C++中虚函数的唯一用处就是构成多态。
工程;
资源和头文件;
#include "resource.h" #include <windows.h> / // // Menu // IDC_VIRTFDEMO MENU BEGIN POPUP "&File" BEGIN MENUITEM "C++虚函数", IDM_virtf MENUITEM "E&xit", IDM_EXIT END POPUP "&Help" BEGIN MENUITEM "&About ...", IDM_ABOUT END END #define IDM_EXIT 10001 #define IDM_ABOUT 10002 #define IDC_VIRTFDEMO 10101 #define IDD_ABOUTBOX 10102 #define IDM_virtf 40001