引言
C 語言通常被認(rèn)為是一種面向過程的語言,因為其本身的特性更容易編寫面向過程的代碼,當(dāng)然也不排除使用 C 語言編寫面向過程的代碼,比如 Linux 的源代碼以及現(xiàn)在很火的國產(chǎn)物聯(lián)網(wǎng)操作系統(tǒng) RT-Thread,其內(nèi)核的實現(xiàn)方式都是使用 C 語言實現(xiàn)的面向?qū)ο蟮拇a。相比于 C 語言來說,C++ 更能夠?qū)崿F(xiàn)面向?qū)ο蟮某绦蛟O(shè)計,其具有的特性也要比 C 語言要多的多。下面假設(shè)有這樣一個需求。
現(xiàn)要描述兩個人的信息,姓名,職業(yè),年齡,并輸出。
我們首先先使用 C 語言的設(shè)計思路實現(xiàn)這個功能。
C語言描述
如果使用 C 語言來描述上面這個問題,大部分都會想到使用結(jié)構(gòu)體來完成這個要求,寫出的程序也就如下所示:
#includestruct person { char *name; int age; char *work; }; int main(int argc, char**?aggv) { struct person persons[]?=?{ {"wenzi",24,"programer"}, ????????{"jiao", 22,"teacher"}, ????}; char i; for (i?= 0;?i?< 2;?i++) ????{ printf("name?is:%s,age?is:%d,work?is:%s\n",persons[i].name,persons[i].age,persons[i].work); ????} }
上述這是比較初級的寫法,如果對 C 語言了解的更多一點的人在寫這段程序的時候,會使用函數(shù)指針的方式將代碼寫的更加巧妙,代碼如下所示:
#includestruct person { char *name; int age; char *work; void (*printInfo)(struct?person?*per); }; void printInfo(struct?person?*per) { printf("The?people's?name?is:%s,age?is:%d,work?is:%s\n",per->name,per->age,per->work); } int main(int argc, char**?argv) { struct person per[2]; per[0]?=?{"wenzi",18,"programer",printInfo}; ????per[1]?=?{"jiaojiao",18,"teacher",printInfo}; ????per[0].printInfo(&per[0]); ????per[1].printInfo(&per[1]); }
使用了函數(shù)指針的方式來書寫這個程序,程序也變得更加簡介了,主函數(shù)里也少了for循環(huán)。
C++ 的引入
那除此之外,還有更好的書寫方式么,這個時候就要引入 C++ 的特性了,上述代碼中在執(zhí)行函數(shù)時都傳入了參數(shù),那要如何做才能將上述中的參數(shù)也省略去呢,且看如下的代碼:
#includestruct person { char *name; int age; char *work; void prinfInfo(void) { printf("The?people's?name?is:%s,age?is:%d,work?is:%s\n",name,age,work);??????? ????} }; int main(int argc, char**?argv) { struct person persons[]?=?{ {"wenzi", 18,"program"}, ????????{"jiao", 18, "teacher"}, ????}; ????persons[0].prinfInfo(); ????persons[1].prinfInfo(); return 0; }
上述代碼中使用了 C++ 的特性,在結(jié)構(gòu)體中定義了函數(shù),然后也就可以直接調(diào)用函數(shù)了,跟上面 C 語言的代碼相比較,它沒了實參,而且代碼看起來也比 C 語言更加簡潔了。
實際在 C++ 中它具有自己獨有的一套機制來實現(xiàn)上述的代碼,也就是即將說明的class,有了 class 之后,我們就可以這樣書寫代碼:
#includeclass person { public: char *?name; int age; char *?work; void printInfo(void) { printf("The?people's?name?is:%s,age?is:%d,work?is:%s\n",name,age,work);? ????} } int main(int argc, char**?argv) { ????person?persons[]?=?{ ????????{"wenzi", 18,"program"}, ????????{"jiao", 18, "teacher"}, ????}; ????persons[0].prinfInfo(); ????persons[1].prinfInfo(); return 0; }
上述就是關(guān)于 C++ 的一個簡單的引入過程。
C++ 數(shù)據(jù)訪問控制
但是為了能夠改變類里的數(shù)據(jù),但是又要使得這個改變不要越界,避免胡亂地改變,我們可以這樣來定義這個類:
#include#include class Person { private: char *name; int age; char *work; public: void PrintInfo(void) { cout << "name?is:" << name << "age?=?"<< age << "work?is:"<< work <<endl; ????} };
這樣定義一個類之后,類里面的數(shù)據(jù)成員就變成了私有的,不能夠在外部進行訪問,比如下面這樣子就是錯誤的:
int main(int argc, char **?argv) { ????Person?per; ????per.age?= 10; //?error }
上述這樣進行數(shù)據(jù)的訪問就是錯誤的,那么要如何進行訪問呢,我們可以定義這樣一個成員函數(shù)進行數(shù)據(jù)的讀寫,比如下面的代碼所示:
#include#include using namespace std; class Person { private: char *name; int age; char *work; public: void PrintInfo(void) { cout << "name?is:" << name << ",age?=?"<< age << ",work?is:"<< work <<endl; ????} void setName(char *n) { ????????name?=?n; ????} int setAge(int a) { if (a?< 0 ||?a?> 150) ????????{ ????????????age?= 0; return 0; ????????} ????????age?=?a; ????} };
這樣定義了類之后,就可以訪問私有成員了,比如下面這樣進行:
int main(int argc, char **argv) { ????Person?per; ????per.setName("wenzi"); ????per.setAge(24); ????per.PrintInfo(); return 0; }
上述代碼加入了private訪問控制符,通過在類里面定義成員函數(shù)的方式,能夠?qū)λ接谐蓡T進行讀寫。
this 指針
再來看上述的代碼,我們可以看到在書寫setName和setAge這兩個函數(shù)的時候,形參寫的是char *n和int a,這樣子給人的感覺就不是那么的直觀,如果寫成char *name和char *age呢,比如成員函數(shù)是像下面這樣子編寫的。
void setName(char *name) { ????name?=?name; } int setAge(int age) { if (age?< 0 ||?age?> 150) ????{ ?????????age?= 0; return 0; ????} ????????age?=?age; }
上述代碼也很容易看出問題,根據(jù) C 語言的就近原則,name = name沒有任何意義,這個時候就需要引入 this 指針。引入 this 指針之后的代碼如下所示:
#include #includeusing namespace std; class Person { private: char *name; int age; char *work; public: void setName(char *name) { this->name?=?name; ????} int setAge(int age) { if (age?< 0 ||?age?> 150) ????????{ this->age?= 0; return -1; ????????} this->age?=?age; return 0; ????} void printInfo(void) { cout << "name?=" << name << ",?age?=" << age << endl; ????} }; int main(int argc, char **argv) { ????Person?per; ????per.setName("wenzi"); ????per.setAge(25); ????per.printInfo(); }
在上述代碼中,引入了 this 指針,通過上述代碼也可以非常清楚它的意思,就是代表當(dāng)前實例化的對象,能夠指向當(dāng)前實例化對象的成員。
程序結(jié)構(gòu)
上述代碼中,成員函數(shù)是在類里面實現(xiàn)的,這樣使得整個類看著十分的臃腫,我們可以按照如下的方式進行書寫:
#includeclass Person { private: char *name; int age; char *work; public: void SetName(char *name); int SetAge(int age;) void PrintInfo(void); } void Person::SetName(char *name) { this->name?=?name; } void Person::SetAge(int age) { this->age?=?age; } void Person::PrintInfo(void) { cout << "name?=?" << name << "age?=?" << age << endl; }
通過在類外面實現(xiàn)我們的成員函數(shù),看起來要更為簡潔一些,上述就是代碼的實現(xiàn)形式。
多文件
上述代碼中,我們都是將代碼寫在一個文件中,這樣當(dāng)代碼量很大的時候,如果代碼都是在一個文件里,那么會使得代碼難以閱讀,這個時候,我們就會將代碼分別放在幾個文件中來進行管理,比如實現(xiàn)上述相同的功能,我們的代碼結(jié)構(gòu)如下圖所示:
其中main.cpp文件中的內(nèi)容如下所示:
#include#include "person.h" int main(int argc, char **argv) { ????Person?per; //per.name?=?"zhangsan"; per.setName("zhangsan"); ????per.setAge(200); ????per.printInfo(); return 0; }
可以看到在上述main.cpp中包含了#include "person.h"頭文件,實際上是在person.h文件中定義了person類,person.h文件的內(nèi)容如下:
#ifndef __PERSON_H__ #define __PERSON_H__ class Person { private: char *name; int age; char *work; public: void setName(char *name); int setAge(int age); void printInfo(void); }; #endif
然后,在person.cpp中定義了成員函數(shù):
#include#include "person.h" void Person::setName(char *name) { this->name?=?name; } int Person::setAge(int age) { if (age?< 0 ||?age?> 150) ????{ this->age?= 0; return -1; ????} this->age?=?age; return 0; } void Person::printInfo(void) { printf("name?=?%s,?age?=?%d,?work?=?%s\n",?name,?age,?work);? }
在有了上述三個文件之后,要如何進行編譯呢,這個時候就需要寫一個Makefile文件,接下來簡單介紹一下Makefile語法。
Makefile
總的來說Makefile的規(guī)則核心就如下所示:
target?...?:prerequisites command ... ...
target也就是一個目標(biāo)文件,可以是Object File,也可以是執(zhí)行文件。還可以是一個標(biāo)簽
prerequisites就是要生成那個target所需要的文件或者是目標(biāo)
command就是make所要執(zhí)行的命令(任意的Shell)
說了核心的東西,來看我們當(dāng)前所編寫的Makefile文件,Makefile文件如下所示:
person:?main.o?person.o g++?-o $@ $^ %.o?:?%.cpp ????g++?-c?-o $@ $< clean: rm?-f?*.o?person
在這里所要明確的一點是這樣的,就是在Makefile中,必須使用 Tab 鍵來進行縮進。然后,需要明確的一個概念是,要使得代碼能夠執(zhí)行,需要經(jīng)過 編譯 -> 鏈接 -> 執(zhí)行,這三個過程才能夠運行,編譯是把源文件編譯成中間代碼,這個中間代碼在 UNIX 是 .o 文件,然后再把大量的 .o 文件合成可執(zhí)行文件,這個過程就是 鏈接,最后,執(zhí)行我們鏈接好的可執(zhí)行文件。
我們來看上述這個Makefile文件,person是最終的可執(zhí)行文件,然后,要生成這個可執(zhí)行文件,需要main.o文件和person.o文件,然后執(zhí)行這個操作需要的是第二條命令,g++ -o $@ $^,其中$@表示的是目標(biāo)文件,$^表示的是所有依賴文件。
然后,緊接著看第三條,%.o : %.cpp,這里表示的是通配符,表示的是所有的 .o 文件和所有的 .cpp 文件,意思就是說要生成的所有的 .o 文件依賴于 .cpp 文件,然后,執(zhí)行的命令是g++ -c -o $@ $<其中表示的是第一個依賴文件。
最后,我們需要清楚,在編譯過程中,生成了一些中間文件以及可執(zhí)行文件,如果我們想要清除掉當(dāng)前生成的文件,那么只需要執(zhí)行make clean就可以清除掉生成的.o文件以及person文件。
函數(shù)重載
C++ 不允許變量重名,但是對于函數(shù)來說,可以允許重載,只要函數(shù)的參數(shù)不同即可,這樣就完成了函數(shù)的重載,直接來看一段關(guān)于函數(shù)重載的代碼:
#include using namespace std; int add(int a, int b) { cout<<"add?int+int"<<endl; return a+b; } int add(int a, int b, int c) { cout<<"add?int+int+int"<<endl; return a+b+c; } double add(double a, double b) { cout<<"add?double+double"<<endl; return a+b; } double add(int a, double b) { cout<<"add?int+double"<<endl; return (double)a+b; } double add(double b, int a) { cout<<"add?double+int"<<endl; return (double)a+b; } int main(int argc, char **argv) { ????add(1, 2); ????add(1, 2, 3); ????add(1.0, 2.0); ????add(1, 2.0); ????add(1.0, 2); return 0; }
代碼很簡單,就是兩數(shù)相加的一個運算,但是兩數(shù)相加的形參不一樣,有的形參是兩個整型的相加,還有是一個整型和浮點數(shù)的相加,因為 C++ 重載的功能,因此,得以定義多個函數(shù)名相同但是形參和返回值都不同的函數(shù),從而在主函數(shù)實現(xiàn)了不同類型數(shù)的相加。
引用和指針
在 C語言中是沒有引用的,在 C++ 中引用的提出也使得之前在 C 語言中必須使用指針的操作,現(xiàn)在可以使用引用完成了,但是引用又不是指針,簡單來說,引用是一個變量的別名,也就是“綽號”,對于這個別名的操作也就完全等同于被引用變量的操作。為了看是否真的是別名,我們來實驗這樣一段代碼:
#include using namespace std; int main(int argc,char **argv) { int m; ????m?= 10; int &n?=?m; int *p?=?&m; int *p1?=?&n; cout << "n?=" << n << endl; cout << "p?=" << p << endl; cout << "p1?=" << p1 << endl; return 0;? }
上述這段代碼中輸出的就是 n 的值,和 m 以及 n 變量的地址,我們來看輸出的內(nèi)容:
可以看到代碼中雖然是對 m 進行了賦值,但是在輸出 n 的時候,輸出的是 m 的值,也就是說在這里對于 n 的操作是完全等同于 m 的,緊接著,我們來證實 n 是否是 m 的別名,那么我們就來看 n 和 m 的地址,可以看到我們輸出的兩個變量的地址也是完全一致的,這也就證實了我們的說法。
接下來,看一段指針,引用,常規(guī)形參的一段代碼,代碼如下所示:
#include using namespace std; int add_one(int a) { ????a?=?a+1; return a; } int add_one(int *a) { ????*a?=?*a?+ 1; return *a; } int add_one_ref(int &b) { ????b?=?b+1; return b; } int main(int argc, char **argv) { int a?= 99; int &c?=?a; cout<endl; cout<<"a?=?"<endl; cout< endl; cout<<"a?=?"<endl; cout< endl; cout<<"a?=?"<endl; ????????c++; cout<<"a?=?"<endl; cout<<"c?=?"< endl; return 0; }
根據(jù)上述對于引用的闡述,我們直接給出運行結(jié)果,運行結(jié)果如下所示:
具體的計算過程就不再這里贅述了。
小結(jié)
OK,上述就是關(guān)于 C++ 的一個簡單的引入的過程以及其涉及到的一部分有別于C語言的語法,本教程將持續(xù)連載,歡迎各位朋友關(guān)注~
本小節(jié)所涉及的代碼可以通過百度云鏈接的方式獲取:鏈接:https://pan.baidu.com/s/1RWPXiqiFCVApcfTdaHyDgw
提取碼:j9hd
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!