適合具備 C 語言基礎(chǔ)的 C++ 教程之一
引言
C 語言通常被認(rèn)為是一種面向過程的語言,因?yàn)槠浔旧淼奶匦愿菀拙帉懨嫦蜻^程的代碼,當(dāng)然也不排除使用 C 語言編寫面向過程的代碼,比如 Linux 的源代碼以及現(xiàn)在很火的國產(chǎn)物聯(lián)網(wǎng)操作系統(tǒng) RT-Thread,其內(nèi)核的實(shí)現(xiàn)方式都是使用 C 語言實(shí)現(xiàn)的面向?qū)ο蟮拇a。相比于 C 語言來說,C++ 更能夠?qū)崿F(xiàn)面向?qū)ο蟮某绦蛟O(shè)計(jì),其具有的特性也要比 C 語言要多的多。下面假設(shè)有這樣一個(gè)需求。
現(xiàn)要描述兩個(gè)人的信息,姓名,職業(yè),年齡,并輸出。
我們首先先使用 C 語言的設(shè)計(jì)思路實(shí)現(xiàn)這個(gè)功能。
C語言描述
如果使用 C 語言來描述上面這個(gè)問題,大部分都會(huì)想到使用結(jié)構(gòu)體來完成這個(gè)要求,寫出的程序也就如下所示:
- #include <stdio.h>
- struct 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);
- }
- }
上述這是比較初級(jí)的寫法,如果對(duì) C 語言了解的更多一點(diǎn)的人在寫這段程序的時(shí)候,會(huì)使用函數(shù)指針的方式將代碼寫的更加巧妙,代碼如下所示:
- #include <stdio.h>
- struct 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ù)指針的方式來書寫這個(gè)程序,程序也變得更加簡介了,主函數(shù)里也少了 for循環(huán)。
C++ 的引入
那除此之外,還有更好的書寫方式么,這個(gè)時(shí)候就要引入 C++ 的特性了,上述代碼中在執(zhí)行函數(shù)時(shí)都傳入了參數(shù),那要如何做才能將上述中的參數(shù)也省略去呢,且看如下的代碼:
- #include <stdio.h>
- struct 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 語言的代碼相比較,它沒了實(shí)參,而且代碼看起來也比 C 語言更加簡潔了。
實(shí)際在 C++ 中它具有自己獨(dú)有的一套機(jī)制來實(shí)現(xiàn)上述的代碼,也就是即將說明的 class,有了 class 之后,我們就可以這樣書寫代碼:
- #include <stdio.h>
- class 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++ 的一個(gè)簡單的引入過程。
C++ 數(shù)據(jù)訪問控制
但是為了能夠改變類里的數(shù)據(jù),但是又要使得這個(gè)改變不要越界,避免胡亂地改變,我們可以這樣來定義這個(gè)類:
- #include <stdio.h>
- #include <iostream>
- class Person
- {
- private:
- char *name;
- int age;
- char *work;
- public:
- void PrintInfo(void)
- {
- cout << "name is:" << name << "age = "<< age << "work is:"<< work <<endl;
- }
- };
這樣定義一個(gè)類之后,類里面的數(shù)據(jù)成員就變成了私有的,不能夠在外部進(jìn)行訪問,比如下面這樣子就是錯(cuò)誤的:
- int main(int argc, char ** argv)
- {
- Person per;
- per.age = 10; // error
- }
上述這樣進(jìn)行數(shù)據(jù)的訪問就是錯(cuò)誤的,那么要如何進(jìn)行訪問呢,我們可以定義這樣一個(gè)成員函數(shù)進(jìn)行數(shù)據(jù)的讀寫,比如下面的代碼所示:
- #include <stdio.h>
- #include <iostream>
- 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;
- }
- };
這樣定義了類之后,就可以訪問私有成員了,比如下面這樣進(jìn)行:
- int main(int argc, char **argv)
- {
- Person per;
- per.setName("wenzi");
- per.setAge(24);
- per.PrintInfo();
- return 0;
- }
上述代碼加入了 private 訪問控制符,通過在類里面定義成員函數(shù)的方式,能夠?qū)λ接谐蓡T進(jìn)行讀寫。
this 指針
再來看上述的代碼,我們可以看到在書寫 setName 和 setAge這兩個(gè)函數(shù)的時(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沒有任何意義,這個(gè)時(shí)候就需要引入 this 指針。引入 this 指針之后的代碼如下所示:
- #include <iostream>
- #include <stdio.h>
- using 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)前實(shí)例化的對(duì)象,能夠指向當(dāng)前實(shí)例化對(duì)象的成員。
程序結(jié)構(gòu)
上述代碼中,成員函數(shù)是在類里面實(shí)現(xiàn)的,這樣使得整個(gè)類看著十分的臃腫,我們可以按照如下的方式進(jìn)行書寫:
- #include <stdio.h>
- class 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;
- }
通過在類外面實(shí)現(xiàn)我們的成員函數(shù),看起來要更為簡潔一些,上述就是代碼的實(shí)現(xiàn)形式。
多文件
上述代碼中,我們都是將代碼寫在一個(gè)文件中,這樣當(dāng)代碼量很大的時(shí)候,如果代碼都是在一個(gè)文件里,那么會(huì)使得代碼難以閱讀,這個(gè)時(shí)候,我們就會(huì)將代碼分別放在幾個(gè)文件中來進(jìn)行管理,比如實(shí)現(xiàn)上述相同的功能,我們的代碼結(jié)構(gòu)如下圖所示:
image-20210110120503456
其中 main.cpp文件中的內(nèi)容如下所示:
- #include <stdio.h>
- #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"頭文件,實(shí)際上是在 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 <stdio.h>
- #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);
- }
在有了上述三個(gè)文件之后,要如何進(jìn)行編譯呢,這個(gè)時(shí)候就需要寫一個(gè) Makefile文件,接下來簡單介紹一下 Makefile語法。
Makefile
總的來說 Makefile的規(guī)則核心就如下所示:
- target ... :prerequisites
- command
- ...
- ...
target也就是一個(gè)目標(biāo)文件,可以是Object File,也可以是執(zhí)行文件。還可以是一個(gè)標(biāo)簽
prerequisites就是要生成那個(gè)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
在這里所要明確的一點(diǎn)是這樣的,就是在 Makefile中,必須使用 Tab 鍵來進(jìn)行縮進(jìn)。然后,需要明確的一個(gè)概念是,要使得代碼能夠執(zhí)行,需要經(jīng)過 編譯 -> 鏈接 -> 執(zhí)行,這三個(gè)過程才能夠運(yùn)行,編譯是把源文件編譯成中間代碼,這個(gè)中間代碼在 UNIX 是 .o 文件,然后再把大量的 .o 文件合成可執(zhí)行文件,這個(gè)過程就是 鏈接,最后,執(zhí)行我們鏈接好的可執(zhí)行文件。
我們來看上述這個(gè) Makefile文件,person是最終的可執(zhí)行文件,然后,要生成這個(gè)可執(zhí)行文件,需要 main.o文件和 person.o文件,然后執(zhí)行這個(gè)操作需要的是第二條命令,g++ -o $@ $^,其中 $@ 表示的是目標(biāo)文件,$^表示的是所有依賴文件。
然后,緊接著看第三條,%.o : %.cpp,這里表示的是通配符,表示的是所有的 .o 文件和所有的 .cpp 文件,意思就是說要生成的所有的 .o 文件依賴于 .cpp 文件,然后,執(zhí)行的命令是 g++ -c -o $@ $<其中表示的是第一個(gè)依賴文件。
最后,我們需要清楚,在編譯過程中,生成了一些中間文件以及可執(zhí)行文件,如果我們想要清除掉當(dāng)前生成的文件,那么只需要執(zhí)行 make clean就可以清除掉生成的 .o文件以及 person文件。
函數(shù)重載
C++ 不允許變量重名,但是對(duì)于函數(shù)來說,可以允許重載,只要函數(shù)的參數(shù)不同即可,這樣就完成了函數(shù)的重載,直接來看一段關(guān)于函數(shù)重載的代碼:
- #include <iostream>
- 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ù)相加的一個(gè)運(yùn)算,但是兩數(shù)相加的形參不一樣,有的形參是兩個(gè)整型的相加,還有是一個(gè)整型和浮點(diǎn)數(shù)的相加,因?yàn)?C++ 重載的功能,因此,得以定義多個(gè)函數(shù)名相同但是形參和返回值都不同的函數(shù),從而在主函數(shù)實(shí)現(xiàn)了不同類型數(shù)的相加。
引用和指針
在 C語言中是沒有引用的,在 C++ 中引用的提出也使得之前在 C 語言中必須使用指針的操作,現(xiàn)在可以使用引用完成了,但是引用又不是指針,簡單來說,引用是一個(gè)變量的別名,也就是“綽號(hào)”,對(duì)于這個(gè)別名的操作也就完全等同于被引用變量的操作。為了看是否真的是別名,我們來實(shí)驗(yàn)這樣一段代碼:
- #include <iostream>
- 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)容:
image-20210112235421638
可以看到代碼中雖然是對(duì) m 進(jìn)行了賦值,但是在輸出 n 的時(shí)候,輸出的是 m 的值,也就是說在這里對(duì)于 n 的操作是完全等同于 m 的,緊接著,我們來證實(shí) n 是否是 m 的別名,那么我們就來看 n 和 m 的地址,可以看到我們輸出的兩個(gè)變量的地址也是完全一致的,這也就證實(shí)了我們的說法。
接下來,看一段指針,引用,常規(guī)形參的一段代碼,代碼如下所示:
- #include <iostream>
- 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<<add_one(a)<<endl;
- cout<<"a = "<<a<<endl;
- cout<<add_one(&a)<<endl;
- cout<<"a = "<<a<<endl;
- cout<<add_one_ref(a)<<endl;
- cout<<"a = "<<a<<endl;
- c++;
- cout<<"a = "<<a<<endl;
- cout<<"c = "<<c<<endl;
- return 0;
根據(jù)上述對(duì)于引用的闡述,我們直接給出運(yùn)行結(jié)果,運(yùn)行結(jié)果如下所示:
image-20210113000240223
具體的計(jì)算過程就不再這里贅述了。
小結(jié)
OK,上述就是關(guān)于 C++ 的一個(gè)簡單的引入的過程以及其涉及到的一部分有別于C語言的語法,本教程將持續(xù)連載,歡迎各位朋友關(guān)注~
本小節(jié)所涉及的代碼可以通過百度云鏈接的方式獲?。烘溄樱篽ttps://pan.baidu.com/s/1RWPXiqiFCVApcfTdaHyDgw
提取碼:j9hd
本文轉(zhuǎn)載自微信公眾號(hào)「wenzi嵌入式軟件」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系wenzi嵌入式軟件公眾號(hào)。