C++封装常用对象和对头文件以及预编译机制的探索
在C++實(shí)際開發(fā)中,難免會使用到一些你極為常用的算法(比如筆者經(jīng)常使用的多線程技術(shù)),實(shí)現(xiàn)這些算法的類或是全局函數(shù)或是命名空間等等經(jīng)常都要被使用多次,你會有哪些辦法來使用呢?筆者有4個(gè)辦法。
第一個(gè)方法就是你直接重新編寫一個(gè)和原來一樣的算法,但是這種方法又費(fèi)時(shí)又費(fèi)力,效率不高,只有初學(xué)者在沒有辦法的時(shí)候才會使用這種方法。第二種方法也是如此,就是復(fù)制一份代碼到新寫的文件中,這種方法的缺點(diǎn)是你不確定能否找到之前的代碼,而且一點(diǎn)也不像IT人員的解決方案。
我們重點(diǎn)介紹第三種和第四種方法。
第三種方法是在需要的地方聲明這個(gè)類或是全局函數(shù)。注意聲明的時(shí)候要使用extern關(guān)鍵字來聲明(任何東西都是如此:全局函數(shù)、類、結(jié)構(gòu)……)比如下面的代碼:
//a.cpp #include <stdio.h> void print(){pringf("HelloWorld"); }//b.cpp extern pingt(); int main(){print();return 0; } 這個(gè)代碼在b.cpp中聲明了一個(gè)a.cpp文件中的函數(shù)并且使用了存儲類聲明符——extern。在這里我先為大家介紹一下C++的存儲類。存儲類說明的是一個(gè)對象的存儲方法,通常有以下4種1、extern 只聲明不定義,比如聲明一個(gè)變量卻不分配內(nèi)存,一般用于兩個(gè)文件中的共享(a文件中定義了一個(gè)類,想要在b文件中訪問,則必須在b文件首部聲明一個(gè)extern的a文件中的類,上面示例使用的就是這種方法);
2、static 靜態(tài),表示把這個(gè)東西定義在堆而不是棧里。全局的對象一般都是靜態(tài)的,但是如果顯示聲明,則表示我發(fā)在其他文件中使用extern來調(diào)用它。如果在一個(gè)本應(yīng)該定義在棧的代碼塊中聲明成static,也將聲明在堆中(比如全局函數(shù));
3、register 注冊,把一個(gè)對象聲明到寄存器中,但是如果使用&操作符在程序中顯示地取地址,編譯器也將把這個(gè)對象定義到內(nèi)存而不是寄存器中。使用register一般可以加快處理速度和減少內(nèi)存使用量。
4、auto 默認(rèn)、自動,聲明一個(gè)對象的默認(rèn)形式,和static正好相反,不能把一個(gè)全局對象聲明成auto。
那么,我們就明白了剛才那段代碼中我們使用extern是為什么了。這種方法很常用,但是并不是最好的方法。
接下來,隆重介紹我們的第四種方法——頭文件!什么?你說你沒有聽說過頭文件(head)?不可能,只要你寫的不是既不輸出也不輸入的console程序,也不是窗口中什么也沒有的程序,那么你就必須用到頭文件!看看你的C++編譯器中include目錄吧,那就是C++語言的標(biāo)準(zhǔn)頭文件庫,里邊有C編寫控制臺程序時(shí)常用的stdio還有C++新標(biāo)準(zhǔn)的iostream等等。沒錯,你猜對了,我們第四種方法就是編寫頭文件。哦哦哦,別把這個(gè)方法看的太難!其實(shí)就是把第三種方法變了變而已……但是的確方便得多。而且有一些封裝之類的思想。
好的,首先我們先來重新了解一下頭文件,如果你是高手,請讓你的思緒回到最開始編寫HelloWorld的時(shí)候。那么,我首先要糾正一個(gè)問題——頭文件的定義。頭文件的定義不是“C++提供給程序員的類庫,是一些API之類的東西……”云云,而是“將一些類、全局函數(shù)、宏等資源集中聲明,并在在include后代替聲明,類似于類庫或叫做封裝。”當(dāng)然,這不是官方的定義,是我根據(jù)個(gè)人理解而得出的。還有一個(gè)誤區(qū)——文件擴(kuò)展名,很多windows新手都認(rèn)為文件擴(kuò)展名很重要,這其實(shí)是windows給我們的一個(gè)錯覺。因?yàn)閣indows總是以擴(kuò)展名定義文件并說明文件使用哪種命令打開,但如果你使用過其他的操作系統(tǒng)就知道了,C++源文件并不需要必須是cpp,當(dāng)然,頭文件也不需要都是h。都是文件,里邊都存儲的是二進(jìn)制代碼,編譯器也能編譯。比如C++標(biāo)準(zhǔn)的頭文件ios啦、cmath等等都沒有擴(kuò)展名。
好了,所有需要的誤區(qū)也都說完了,我們來說第四種方法。首先看一個(gè)例程:
//a.cpp #include <iostream> using namespace std; void print(){cout<<"HelloWorld"; }//main.cpp int main(){print(); }你有什么辦法讓編譯器不報(bào)錯呢?現(xiàn)在我們來編寫一個(gè)頭文件。
print();沒錯,就一行,這就是一個(gè)頭文件,我們把它保存成c.h。然后把上面的a.cpp和main.cpp改一下。
//a.cpp #include <iostream> #include "c.h" //注意:要把這三個(gè)文件放在同一目錄using std::cout;void print(){cout<<"HelloWorld"; }//main.cpp #include "c.h"int main(){print();return 0; }OK!你有沒有發(fā)現(xiàn)你成功了呢?而且,你可以編寫頭文件了!(如果你失敗了,聯(lián)系我的qq:2276768747,驗(yàn)證全寫本文連接地址)
接下來,我來先講講include這個(gè)預(yù)編譯指令。什么?#include嗎?我早學(xué)過了。是的,我先說的東西80%的人都知道,但是接著,我要說的80%的人不知道:#include我們一般使用兩種語法——1、#include <{編譯器系統(tǒng)include目錄下的文件}>;2、#include “{本目錄下或是編譯器include目錄的頭文件}” 注意到他們的不同了嗎?他們的不同是<>和””還有,第一種只在include目錄下尋找頭文件,而第二種在當(dāng)前目錄和include目錄下尋找頭文件。這個(gè)功能使得引入自行編寫的頭文件成為可能(沒有也可以,可以放到include目錄下),所以我們可以將聲明甚至是定義放到頭文件里面。還有一種情況,就是把聲明和定義全部放到頭文件里面,這樣可以避免在使用模板和泛型的時(shí)候出現(xiàn)問題,請看下面的代碼:
//c.h //把聲明和定義全部放在頭文件里,可以使用模板template #include <iostream>using namespace std;void print(){cout<<"HelloWorld"; }//main.cpp #include "c.h"int main(){print();return 0; }還有一些我要說:我們可以繼續(xù)模仿C++開發(fā)者——讓我們編寫的頭文件更“正式”(當(dāng)然,不是給你講那成山的注釋是怎么回事,更不是強(qiáng)逼迫你那么做,我要講述一些頭文件中經(jīng)常使用的預(yù)編譯指令)。首先我要給大家一個(gè)詳細(xì)的表格:
#空指令,無任何效果
#include包含一個(gè)源代碼文件
#define定義宏
#undef取消已定義的宏
#if如果給定條件為真,則編譯下面代碼
#ifdef如果宏已經(jīng)定義,則編譯下面代碼
#ifndef如果宏沒有定義,則編譯下面代碼
#elif如果前面的#if給定條件不為真,當(dāng)前條件為真,則編譯下面代碼
#endif結(jié)束一個(gè)#if……#else條件編譯塊
#error停止編譯并顯示錯誤信息
接下來,我一點(diǎn)點(diǎn)的解釋上面的宏定義指令,他可以讓你的頭文件具有判斷功能并且使你的頭文件變得更加正規(guī)和靈活。
首先,我們的老朋友#include,我們已經(jīng)深入了解過了include的所有“底細(xì)”,在此不再贅述。
然后我們來看看#define。#define在C語言編程中很常用,一般用于定義一些具有特殊意義的全局函數(shù)或者常量,其正式名稱是“宏定義語句”,我們并不多講,只舉一個(gè)例子:
#define PI 3.14 //定義π的值,常量接著看#undef,它是#define的逆運(yùn)算,即消除宏定義:
#define PI 3.14 //PI可用 #undef PI //PI不再可用#if是條件判斷語句,這個(gè)是判斷條件是否為真,為真執(zhí)行#if到#endif之內(nèi)的語句然后執(zhí)行下方語句,否則直接執(zhí)行下方語句,這個(gè)在頭文件中傳輸信息時(shí)用的比較多,重點(diǎn)講一下:
#define DEBUG 0 //定義DEBUG,為0,在C++中,0代表假,1代表真main() { #if DEBUG //所以顯然這里的條件是假,那么就不會輸出Debugging(當(dāng)然我不想老調(diào)試) printf("Debugging "); #endif //之后的語句將執(zhí)行,也就是說程序執(zhí)行后會輸出Running printf("Running "); }//如果把define改成#define DEBUG 1,將會輸出: //Debugging //Running我最開始也不能理解,但是用得多了,就能夠很好地理解這個(gè)#if,其實(shí)它和我們C++的if語句一樣,下面會講到else……(實(shí)質(zhì)上我也不知道會在哪里用到,只是寫封裝的時(shí)候避免一些重復(fù)引用的問題云云)
接著說一下ifdef和ifndef,這里注意:#ifdefined等價(jià)于#ifdef;#if!defined等價(jià)于#ifndef。也就是說ifdef的意思是判斷是否定義,如果定義了,就執(zhí)行下面語句,否則不執(zhí)行(else除外)而ifndef正好相反。話不多少看示例:
#define DEBUG //注意,這里定義了DEBUGmain() { #ifdef DEBUG //很顯然,這是真,將會輸出yes printf("yes "); #endif #ifnde fDEBUG //DEBUG沒定義嗎?當(dāng)然定義了,所以no不會輸出 printf("no "); #endif } //輸出結(jié)果:yes //如果注釋掉第一句,將會輸出no好了,我們終于可以講else了(我不希望你因?yàn)椴荒蜔┖妥砸詾檎莆樟朔庋b而不看下去,否則你將吃大虧的!你的頭文件總會出現(xiàn)重名、重復(fù)引用之類的東西,由于頭文件沒有main主函數(shù),只能通過預(yù)處理來進(jìn)行判斷條件等操作)。看看else,前面我就說了,預(yù)處理和C++語法一樣,所以這代表“否則”的意思。一行代碼+注釋遠(yuǎn)遠(yuǎn)大于100行解釋+說明(我的名言,記住了,先記住了,然后仔細(xì)看示例):
#define DEBUG //定義了DEBUGmain() { #ifndef DEBUG //剛才說過的,不多解釋,將不會會輸出NoDebugging printf("Nodebugging "); #else //否則,也就是說定義了DEBUG的時(shí)候,將會輸出Debugging printf("Debugging "); #endif printf("Running "); //在endif之后,將會輸出RUnning } //輸出結(jié)果: //Debugging //Running這個(gè)也很簡單吧!接下來,我們回憶一下我們用過的if……else if……這種語句格式。在判斷性預(yù)編譯機(jī)制中,我們也有這種類似的語句——#elif。不多說了,我們來看看示例代碼:
#define TWO //定義一個(gè)TWO,記住了!main() { #ifdef ONE //這里是判斷是否定義ONE,所以不會輸出1 printf("1 "); #elifdefined TWO //我不知道這里是否能夠縮寫成elifdef,但是,的意思是else if defined TWO,也就是會輸出2 printf("2 "); #else //否則,這里不會執(zhí)行,如果不懂的話就想想else if語句塊 printf("3 "); #endif //預(yù)編譯判斷結(jié)束 }接著是這個(gè):
#error指令將使編譯器顯示一條錯誤信息,然后停止編譯。
#line指令可以改變編譯器用來指出警告和錯誤信息的文件號和行號。
#pragma指令沒有正式的定義。編譯器可以自定義其用途。典型的用法是禁止或允許某些煩人的警告信息。
這個(gè)很簡單了,我就不再多說了,看網(wǎng)上的定義就可以了,這個(gè)error還挺常用的,仔細(xì)看一下,有興趣的網(wǎng)上搜搜也無妨。接著我淺談一下C++預(yù)處理機(jī)制對我們的封裝的作用:
1、由于我們封裝的頭文件沒有main方法,不能對于程序的執(zhí)行的流程和引用文件等操作進(jìn)行流程控制;
2、由于C++中頭文件會被自動inline,所以需要預(yù)處理一些引用以便避免重名和重復(fù)引用;
3、我們編寫頭文件封裝的時(shí)候難免會使用命名空間和類、結(jié)構(gòu)之類的東西,所以我們一般需要控制生成和調(diào)用流程以便保證使用封裝時(shí)的安全。這里多說一下:在if系列中的預(yù)處理語句中,除了可以放輸出,還可以放define甚至需要限定某些條件下會執(zhí)行的語句,所以說,#if系列有的時(shí)候也被叫做“頭文件的if語句”。
希望大家掌握上述知識,靈活運(yùn)用,以便使得將來的項(xiàng)目(過去的就算了,先別改了)更加穩(wěn)定、靈活、高效。
?
在此特別聲明:本文中有關(guān)于預(yù)處理的例程和定義均來自http://www.kuqin.com/language/20090806/66164.html,但是為其添加了注釋并修改了格式,筆者為時(shí)間倉促沒有另行通知而梳表歉意,如原作者不希望筆者使用,請與我聯(lián)系:zhangyutong@zhangyutong.net。感謝各位,文章最后我將留下我的聯(lián)系方式。
QQ:2276768747? MSN:xztzrjcxxzz@hotmail.com? email:zhangyutong@zhangyutong.net 感謝閱讀金雞獨(dú)立提供的計(jì)算機(jī)技術(shù)文章!再見!
轉(zhuǎn)載于:https://www.cnblogs.com/zhangyutong926/p/3599280.html
總結(jié)
以上是生活随笔為你收集整理的C++封装常用对象和对头文件以及预编译机制的探索的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 菜根谭#80
- 下一篇: 为何安卓手机没有iPhone流畅