C++基礎之純虛函數與抽象類
引言
純虛函數在C++編程中的地位很重要,其關聯到了設計模式中“接口”的概念。
語法
純虛函數的語法:
1、 將成員函數聲明為virtual
2、 后面加上 = 0
3、 該函數沒有函數體
1 class <類名> 2 { 3 virtual <類型><函數名>(<參數表>) = 0; 4 … 5 };
例如:
1 class CmdHandler 2 { 3 virtual void OnCommand(char* cmdline) = 0; 4 … 5 };
在許多情況下,在基類中不能對虛函數給出有意義有實現,而把它說明為純虛函數,它的實現留給該基類的派生類去做。這就是純虛函數的作用。
抽象類
含有純虛函數的類叫做抽象類(純虛類),抽象類是一種特殊的類,它是為了抽象和設計的目的而建立的,它處於繼承層次結構的較上層。
抽象類不能被實例化,即無法創建該類的對象。
CmdHandler ch; // 編譯錯誤!!
CmdHandler *p = new CmdHandler(); // 編譯錯誤!!
在實際中為了強調一個類是抽象類,可將該類的構造函數說明為保護的訪問控制權限。
抽象類的主要作用是將有關的組織在一個繼承層次結構中,由它來為它們提供一個公共的根,相關的子類是從這個根派生出來的。
抽象類刻畫了一組子類的操作接口的通用語義,這些語義也傳給子類。一般而言,抽象類只描述這組子類共同的操作接口,而完整的實現留給子類。
抽象類只能作為基類來使用,其純虛函數的實現由派生類給出。如果派生類沒有重新定義純虛函數,而派生類只是繼承基類的純虛函數,則這個派生類仍然還是一個抽象類。如果派生類中給出了基類純虛函數的實現,則該派生類就不再是抽象類了,它是一個可以建立對象的具體類了。
接口
實際用途:充當“接口函數”
(相當於Java中的interface語法)
(用於替代C中的回調函數的用法)
接口規范:凡是遵循此規范的類,都必須實現指定的函數接口,通常是一系列接口。
上述定義的抽象類可以理解為:凡是遵循CmdHandler規范的類,都必須實現指定的函數接口:OnCommand()。
一個實例
需求:用戶輸入一行命令,按回車完成輸入。要求解析命令的輸入,並處理之。
設計:
CmdInput:用於接收用戶的輸入
CmdHandler:規定一系列函數接口
CmdParser:接口的實現,實際用於解析的處理類
說明:
可見,CmdInput只接收輸入的內容,而CmdParser用於對輸入的內容進行解析,兩個類各做各的,互不干涉,兩者並不知道對方的存在,而是通過抽象類CmdHandler充當“接口”聯系起來。
代碼:
// main.cpp #include “CmdInput.h” #include “CmdParser.h” int main(void) { CmdInput CInput; CmdParser CParser; CInput.SetHandler(&CParser); CInput.Run(); return 0; } // CmdHandler.h /* CmdHandler 接口類*/ class CmdHandler { public: virtual ~CmdHandler() {} // 析構函數聲明為 virtual virtual void OnCommand(char* cmdline) = 0; // 純虛函數 }; // CmdInput.h #include “CmdHandler.h” class CmdInput { public: CmdInput(); void SetHandler(CmdHandler* pCHandler); int Run(); private: CmdHandler* m_pCHandler; }; // CmdInput.cpp #include “CmdInput.h” CmdInput::CmdInput () { m_pCHandler = NULL; } void CmdInput:: SetHandler(CmdHandler* pCHandler) { m_pCHandler = pCHandler; } int CmdInput::Run() { char cmdline[256]; memset(cmdline, 0, 256); while(1) { printf("> "); // 輸入 gets(cmdline); if(strcmp(cmdline, "exit") == 0) // 退出 { break; } if(m_handler) // 解析與執行 { m_handler->OnCommand(cmdline); } } return 0; } // CmdParser.h #include “CmdHandler.h” /* MyParser: 一個遵循了CmdHandler接口的類*/ class MyParser : public CmdHandler { public: MyParser(); public: virtual void OnCommand(char* cmdline); // 函數接口集 private: int Split(char text[], char* parts[]); // 解析命令 }; // CmdParser.cpp #include <stdio.h> #include “CmdParser.h” CmdParser::CmdParser () { } void CmdParser::OnCommand(char* cmdline) { char* argv[128]; int argc = Split(cmdline,argv); if(argc > 0) { printf("命令: %s \n" , argv[0]); printf("參數: "); for(int i=1; i<argc; i++) { printf("%s ", argv[i]); } printf("\n\n"); } } int CmdParser::Split(char text[], char* parts[]) { int count = 0; // 分段的個數 int start = 0; // 每一分段的首地址 int flag = 0; // 遍歷text,標識當前是否處於有效字符 int stop = 0; // 是否到達結束 for(int i=0; !stop ; i++) { char ch = text[i]; if(ch == 0) stop = 1; // 結束循環 if(ch == ',' || ch == '\0' || ch == ' ' || ch == '\t' ) { if(flag) // 遇到分隔符,且當前狀態為flag=1 { flag = 0; text[i] = 0; // 修改為結束符,完成分段 parts[count] = text + start; // 記錄首地址 count ++; } } else { if(!flag) // 遇到有效字符,且當前狀態為flag=0 { flag = 1; start = i; } } } return count; //返回分段個數 }
小結
1、 純虛函數的定義
2、 抽象類及其實質作用:接口規范,因為它只代表了一個規范,並沒有具體實現,所以它不能被實例化。
3、 抽象類通常被多重繼承,例如,一個普通的類,實現了多套接口規范,又繼承於原有的父類。
4、 抽象類的析構函數應該聲明為virtual,因為它被涉及用於繼承的。