C++中新手容易犯的十種編程錯(cuò)誤匯總
前言
IT公司每年都會(huì)有一定的人員流動(dòng),相應(yīng)地也會(huì)招一些應(yīng)屆生補(bǔ)充進(jìn)來(lái),指導(dǎo)應(yīng)屆生已經(jīng)成為老員工的必修課了。平日里會(huì)我們會(huì)經(jīng)常幫新人排查代碼中的問(wèn)題,在此過(guò)程中發(fā)現(xiàn)了C++新手容易犯的一些編程錯(cuò)誤,在此簡(jiǎn)單的總結(jié)一下,給新人們提供一個(gè)參考。
1、有些關(guān)鍵字在cpp文件中多寫了
對(duì)于C++類,一些關(guān)鍵字只要寫在.h中就好,cpp中就不用再加上了,比如virtual、static等關(guān)鍵字,如果再cpp中多寫,編譯器會(huì)報(bào)錯(cuò)。比如如下的虛接口與靜態(tài)成員變量的定義,只要在頭文件中聲明就可以了。
class shape { virtual Draw(); //... static int nLevel; }
2、函數(shù)參數(shù)的默認(rèn)值寫到函數(shù)實(shí)現(xiàn)中了
帶有參數(shù)默認(rèn)值的函數(shù),默認(rèn)值是加在函數(shù)聲明處的,函數(shù)實(shí)現(xiàn)處的參數(shù)是不需要帶上的。為了方便查看代碼,在函數(shù)實(shí)現(xiàn)處的參數(shù)中,將默認(rèn)值注釋起來(lái)。正確的做法是,頭文件中有默認(rèn)值:
BOOL CreateConf( const CString& strConfName, const BOOL bAudio = FALSE );
在函數(shù)實(shí)現(xiàn)處的參數(shù)中不用添加默認(rèn)值:
BOOL CreateConf( const CString& strConfName, const BOOL bAudio/* = FALSE*/ ); { // ...... }
3、在編寫類的時(shí)候,在類的結(jié)尾處忘記添加";"分號(hào)了
在類的結(jié)尾處忘記添加分號(hào),編譯會(huì)報(bào)錯(cuò),新人們有可能找了半天也沒找出引起編譯錯(cuò)誤的原因。其實(shí)很簡(jiǎn)單,在類的結(jié)尾處忘記添加分號(hào)了。
class Shape { // ... };
4、只添加了函數(shù)聲明,沒有函數(shù)實(shí)現(xiàn)
在添加類的函數(shù)時(shí),只在類的頭文件中添加了函數(shù)聲明,但在cpp中卻沒有添加函數(shù)的實(shí)現(xiàn)。如果其他地方調(diào)用到該函數(shù),在編譯鏈接的時(shí)候會(huì)報(bào) unresolved external symbol錯(cuò)誤。因?yàn)闆]有實(shí)現(xiàn),所有沒有供鏈接使用的obj文件。
5、cpp文件忘記添加到工程中,導(dǎo)致沒有生成供鏈接使用的obj文件
在添加C++類時(shí),我們一般會(huì)添加.h頭文件和一個(gè).cpp源文件。結(jié)果忘記把.cpp文件添加到工程中了,即沒有參與編譯,沒有生成供鏈接使用的obj文件。如果有代碼調(diào)用到該C++類的接口,則在編譯鏈接的時(shí)候會(huì)報(bào) unresolved external symbol錯(cuò)誤,即鏈接不到該C++類對(duì)應(yīng)的接口。
6、函數(shù)中返回了一個(gè)局部變量的地址或者引用
在函數(shù)中返回了一個(gè)局部變量的地址或者引用,而這個(gè)局部變量在函數(shù)結(jié)束時(shí)其生命周期就結(jié)束了,內(nèi)存就被釋放了。當(dāng)外部訪問(wèn)到該變量的內(nèi)存,會(huì)觸發(fā)內(nèi)存訪問(wèn)違例的異常,因?yàn)樵撟兞康膬?nèi)存已經(jīng)釋放了。比如如下的錯(cuò)誤代碼:
char* GetResult() { char chResult[100] = { 0 }; // ...... return chResult; }
7、忘記將父類中的接口聲明virtual函數(shù),導(dǎo)致多態(tài)沒有生效
代碼中本來(lái)要借助于C++多態(tài)的虛函數(shù)調(diào)用,調(diào)用子類實(shí)現(xiàn)的接口,結(jié)果忘記在父類中將對(duì)應(yīng)的接口聲明為virtual,導(dǎo)致沒有調(diào)用到子類實(shí)現(xiàn)的函數(shù)。一定要記住,要實(shí)現(xiàn)多態(tài)下的函數(shù)調(diào)用,父類的相關(guān)接口必須聲明為virtual。
class Shape() { // ... virtual void Draw(); // ... }
8、該使用雙指針的地方,卻使用了單指針
有時(shí)我們需要調(diào)用一個(gè)接口去獲取某些數(shù)據(jù),接口中將數(shù)據(jù)拷貝到傳入的參數(shù)對(duì)應(yīng)的內(nèi)存中,此時(shí)設(shè)計(jì)參數(shù)時(shí)會(huì)傳入指針或引用。我們?cè)谡{(diào)用GetData之前定義了結(jié)構(gòu)體指針p,并new出了對(duì)應(yīng)的結(jié)構(gòu)體對(duì)象內(nèi)存,應(yīng)該在定義GetData接口時(shí)應(yīng)該使用雙指針(指針的指針)的,結(jié)果錯(cuò)寫成了單指針。
有問(wèn)題的代碼如下:
struct CodecInfo // 編碼信息 { int nFrameRate; // ... } CodecInfo* pInfo = new CodecInfo; GetAudioCodecPtr()->GetCodecInfo(pInfo); // 調(diào)用AudioCodec::GetCodecInfo獲取編碼信息 AudioCodec::GetCodecInfo( CodecInfo* pInfo) // 此處的參數(shù)不應(yīng)該使用單指針 { memcpy(pInfo, m_codecInfo, sizeof(CodecInfo)); }
上圖中的AudioCodec::GetCodecInfo接口的參數(shù)不應(yīng)該為單指針,應(yīng)該用雙指針,修改后的代碼應(yīng)該如下:
AudioCodec::GetCodecInfo( CodecInfo** pInfo) // 此處的參數(shù)類型使用雙指針 { memcpy(*pInfo, m_codecInfo, sizeof(CodecInfo)); }
9、發(fā)布exe程序時(shí),忘記將exe依賴的C運(yùn)行時(shí)庫(kù)和MFC庫(kù)帶上
比如新人用VS-MFC庫(kù)編寫一個(gè)測(cè)試用的工具軟件,結(jié)果在發(fā)布release版本程序時(shí),沒有將程序依賴的C運(yùn)行時(shí)庫(kù)帶上,導(dǎo)致該工具軟件在某些電腦中啟動(dòng)報(bào)錯(cuò),提示找不到C運(yùn)行時(shí)庫(kù):
因?yàn)槌绦蛑幸蕾嚵藙?dòng)態(tài)版本的運(yùn)行時(shí)庫(kù)和MFC庫(kù),在發(fā)布程序時(shí)要將這些庫(kù)帶上。有些系統(tǒng)中沒有這些庫(kù),程序啟動(dòng)時(shí)就會(huì)報(bào)找不到庫(kù),就會(huì)啟動(dòng)失敗。
10、應(yīng)該使用深拷貝,卻使用了淺拷貝
本來(lái)應(yīng)該要進(jìn)行深拷貝的,卻使用了淺拷貝(直接賦值),導(dǎo)致另個(gè)不同生命周期的C++對(duì)象指向了同一塊內(nèi)存,一個(gè)對(duì)象將內(nèi)存釋放后,另一個(gè)對(duì)象再用到這塊內(nèi)存,就造成了內(nèi)存訪問(wèn)違例,產(chǎn)生異常。
有個(gè)經(jīng)典的C++筆試題,讓我們實(shí)現(xiàn)String類的相關(guān)函數(shù),其主要目的就是用來(lái)考察對(duì)深拷貝與淺拷貝的理解的。題目中給出String類的聲明:
class String{ public: String(); String(const String & str); String(const char* str); String& operator=(String str); char* c_str() const; ~String(); int size() const; private: char* data; };
讓寫出上述幾個(gè)函數(shù)的內(nèi)部實(shí)現(xiàn)。這些函數(shù)的實(shí)現(xiàn)代碼如下:
//普通構(gòu)造函數(shù) String::String(const char *str) { if (str == NULL) { m_data = new char[1];// 得分點(diǎn):對(duì)空字符串自動(dòng)申請(qǐng)存放結(jié)束標(biāo)志'\0'的,加分點(diǎn):對(duì)m_data加NULL判斷 *m_data = '\0'; } else { int length = strlen(str); m_data = new char[length + 1];// 若能加 NULL 判斷則更好 strcpy(m_data, str); } } // String的析構(gòu)函數(shù) String::~String(void) { delete[] m_data; // 或delete m_data; } //拷貝構(gòu)造函數(shù) String::String(const String &other)// 得分點(diǎn):輸入?yún)?shù)為const型 { int length = strlen(other.m_data); m_data = new char[length + 1];// 若能加 NULL 判斷則更好 strcpy(m_data, other.m_data); } //賦值函數(shù) String & String::operator = (const String &other) // 得分點(diǎn):輸入?yún)?shù)為const型 { if (this == &other)//得分點(diǎn):檢查自賦值 return *this; if (m_data) delete[] m_data;//得分點(diǎn):釋放原有的內(nèi)存資源 int length = strlen(other.m_data); m_data = new char[length + 1];//加分點(diǎn):對(duì)m_data加NULL判斷 strcpy(m_data, other.m_data); return *this;//得分點(diǎn):返回本對(duì)象的引用 }
總結(jié)
到此這篇關(guān)于C++中新手容易犯的十種編程錯(cuò)誤的文章就介紹到這了,更多相關(guān)C++新手易犯編程錯(cuò)誤內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言錯(cuò)誤信息報(bào)告strerror函數(shù)和perror函數(shù)詳解
這篇文章主要介紹了C語(yǔ)言錯(cuò)誤信息報(bào)告strerror函數(shù)和perror函數(shù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03Matlab實(shí)現(xiàn)黑洞優(yōu)化算法的示例代碼
根據(jù)黑洞現(xiàn)象原理首次提出BH 算法,它在傳統(tǒng)PSO基礎(chǔ)上引入了新的機(jī)制,有效地提高了收斂速度并防止了陷入局部極值的情況發(fā)生.本文將用Matlab實(shí)現(xiàn)這一算法,需要的可以參考一下2022-06-06C++程序內(nèi)存棧區(qū)與堆區(qū)模型案例分析
一直以來(lái)總是對(duì)這個(gè)問(wèn)題的認(rèn)識(shí)比較朦朧,我相信很多朋友也是這樣的,總是聽到內(nèi)存一會(huì)在棧上分配,一會(huì)又在堆上分配,那么它們之間到底是怎么的區(qū)別呢,讓我們一起來(lái)看看2022-03-03Qt實(shí)現(xiàn)可以計(jì)算大數(shù)的簡(jiǎn)單計(jì)算器
計(jì)算器是我們生活中很常見的東西,它可以由多種語(yǔ)言多種方式來(lái)實(shí)現(xiàn)。本文主要介紹的是基于C++語(yǔ)言,由QT實(shí)現(xiàn)的可以計(jì)算大數(shù)的簡(jiǎn)單計(jì)算器,需要的可以參考一下2022-12-12