適合具備 C 語言基礎(chǔ)的 C++ 教程之三
前言
在上一則教程中,著重地闡述了構(gòu)造函數(shù)以及析構(gòu)函數(shù)的相關(guān)概念,這也是C++中非常重要的兩個概念之一。在今天的教程中,筆者將繼續(xù)敘述 C++相對于 C語言來說不同的點,將詳細敘述命名空間,靜態(tài)成員,友元函數(shù)以及運算符重載這幾個知識點。
C++ 命名空間
命名空間的存在是為了區(qū)分不同庫的相同的函數(shù)名,用一個簡單的例子來說明這個問題就是在 windows的文件系統(tǒng)中,不同文件夾下可以有相同名字的文件,相同文件夾下因為這相同文件處在不同的范圍內(nèi),用 C++ 說白了也就是處在不同的命名空間中。文件系統(tǒng)的一個結(jié)構(gòu)圖:
文件系統(tǒng)框圖
定義命名空間
命名空間的定義使用的是關(guān)鍵字 namespace,后跟命名空間的名稱,如下所示:
- namespace namespace_name{
- // 代碼聲明
- }
為了調(diào)用帶有命名空間的函數(shù)或者變量,需要在前面加上命名空間的名稱,如下所示:
- name::code // code 可以是變量或者是函數(shù)
例子
下面通過一個例子來說明命名空間的概念,首先,我們具有兩個類,一個是 Dog ,一個是 Person,而這個時候,有兩個函數(shù)具有相同的名字,都要輸出不同的信息,這個時候,就有必要使用到命名空間的概念。首先,我們在 dog.h 里面定義一個 dog 類,代碼如下所示:
- #ifndef __DOG_H__
- #define __DOG_H__
- namespace C{
- class Dog{
- private:
- char *name;
- int age;
- public:
- void setName(char *name);
- int setAge(int age);
- void printInfo(void);
- };
- void printVersion(void);
- }
- #endif
然后,緊接著來看 dog.cpp 里面的內(nèi)容。代碼如下所示:
- #include "dog.h"
- namespace C{
- void Dog::setName(char *name)
- {
- this->name = name;
- }
- int Dog::setAge(int age)
- {
- if (age < 0 || age > 20)
- {
- this->age = 0;
- return -1;
- }
- this->age = age;
- return 0;
- }
- void Dog::printInfo(void)
- {
- printf("name = %s, age = %d\n",name,age);
- }
- void printersion(void)
- {
- printf("Dog v1");
- }
- }
OK ,看完了 Dog 的代碼,我們緊接著來看 Person 的代碼,代碼如下所示:
- #ifndef __PERSON_H__
- #define __PERSON_H__
- namespace A{
- class Person{
- private:
- char *name;
- int age;
- char *work;
- public:
- void setName(char *name);
- int setAge(int age);
- void printInfo(void);
- };
- void printfVersion(void);
- }
- #endif
緊接著就是 Person.cpp 的代碼,具體的代碼如下所示:
- namespace A {
- 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);
- }
- void printVersion(void)
- {
- printf("Person v1\n");
- }
- }
上述就是 所定義的兩個類,我們緊接著來看 main.cpp 的代碼:
- int main(int argc, char **argv)
- {
- A::Person per;
- per.setName("zhangsan");
- per.setAge(16);
- per.printInfo();
- C::Dog dog;
- dog.setName("wangcai");
- dog.setAge(1);
- dog.printInfo();
- A::printVersion();
- C::printVersion();
- return 0
- }
在最后的倒數(shù)第二行和倒數(shù)第三行,我們可以看到如果這個時候,沒有命名空間的存在,那么就完全不能夠分辨 printVersion這個函數(shù),加上了命名空間之后,就能夠分辨出來了。
靜態(tài)成員
在上述代碼的基礎(chǔ)上,我們在主函數(shù)定義了如何幾個變量,代碼如下所示:
- #include <stdio.h>
- int main(int argc, char **argv)
- {
- Person per1;
- Person per2;
- Person per3;
- Person per4;
- Person *per5 = new Person[10];
- }
那我們要如何知道我們定義幾個 Person 對象呢,可以這樣去做,我們創(chuàng)建一個 cnt變量,然后在每個構(gòu)造函數(shù)執(zhí)行的過程中讓 cnt加一,代碼如下所示:
- #include <iostream>
- #include <string.h>
- #include <unistd.h>
- class Person
- {
- private:
- int cnt;
- char *name;
- int age;
- char *work;
- public:
- Person()
- {
- name = NULL;
- work = NULL;
- cnt++;
- }
- Person(char *name)
- {
- this->name = new char[strlen(name) + 1];
- strcpy(this->name, name);
- this->work = NULL;
- cnt++;
- }
- Person(char *name, int age, char *work = "none")
- {
- this->name = new char[strlen(name) + 1];
- strcpy(this->name, name);
- this->work = new char[strlen(work) + 1];
- strcpy(this->work, work);
- cnt++;
- }
- ~Person()
- {
- if (this->name)
- {
- cout << "name is:" << name << endl;
- delete this->name;
- }
- if (this->work)
- {
- cout << "work is:" << work << endl;
- delete this->work;
- }
- }
- };
但是如果這么寫的話存在一個問題,就是我們想要實現(xiàn)的功能是看有幾個實例化 Person 對象,那么這個計數(shù)量cnt應(yīng)該是屬于 Person類的,具體的關(guān)系如下圖所示:
但是上述的代碼中,cnt是屬于 Person的實例化對象的,那要如何做才能使得 cnt屬于 Person類的實例化對象呢,這個時候,我們需要將 cnt定義為 static類的,這樣子,cnt就是屬于 Person類的了,定義的代碼如下所示:
- class Person
- {
- private:
- char *name;
- int age;
- char *work;
- static int cnt;
- };
那么我們要如何得到 cnt 的值呢,可以編寫一個函數(shù),但是同樣的,我們編寫的函數(shù)要是屬于整個 Person類的,那應(yīng)該如何去做呢,同樣的辦法,我們在前面加上 static,代碼如下所示:
- #include <stdio.h>
- #include <iostream>
- class Person
- {
- private:
- char *name;
- int age;
- char *work;
- static int cnt;
- public:
- static int getcount(void)
- {
- return cnt;
- }
- };
有了 getcount函數(shù),我們就可以調(diào)用它,然后將其打印出來,方法如下所示:
- #include <iostream>
- int main(int argc, char *argv)
- {
- Person per1;
- Person per2;
- Person *per5 = new Person[10];
- count << "person number = " << Person:getcount() << endl;
- }
最后,還存在一個問題,因為我們在 cnt上加了 static,那么當前的 cnt就是屬于 Person類的,這樣一來,那么就是說 cnt的值還沒有分配空間,那么要如何分配空間呢,我們需要在主函數(shù)開始之前對 cnt進行定義和初始化,代碼如下所示:
- int Person::cnt = 0; /* 定義*/
這樣的話,就可以知道 cnt的值了,下面是運行的結(jié)果:
這樣,就知道了 Person 類的實例化次數(shù)。那為什么要把 int Person::cnt = 0放在 main函數(shù)的最開始呢,這是因為要在 main所有實例化對象定義之前就要將其初始化完成。
友元函數(shù)
首先,我們有這樣一個需求,需要實現(xiàn)兩個類的相加,下面是寫出來的代碼:
- #include <iostream>
- #include <string.h>
- #include <unistd.h>
- using namespace std;
- class Point
- {
- private:
- int x;
- int y;
- public:
- Point(){}
- Point(int x, int y) : x(x), y(y) {}
- void setX(int x)
- {
- this->x = x;
- }
- void setY(int y)
- {
- this->y = y;
- }
- int getX(void)
- {
- return x;
- }
- int getY(void)
- {
- return y;
- }
- };
- Point add(Point &p1, Point &p2)
- {
- Point n;
- n.setX(p1.getX() + p2.getX());
- n.setY(p1.getY() + p2.getY());
- return n;
- }
- int main(int argc, char **argv)
- {
- Point p1(1, 2);
- Point p2(2, 4);
- Point result = add(p1,p2);
- cout << "the result is:" << "(" << result.getX() << "," << result.getY() << ")"<< endl;
- return 0;
- }
上述代碼中存在一個缺點就是說,我們在進行 add()函數(shù)編寫的時候,用到了兩次 getX()和 getY(),這樣就顯得代碼看起來十分的臃腫,所以也就有了如下的更改方式,我們可以將 Point add(Point &p1, Point &p2)函數(shù)設(shè)置成友元,那么在這樣的基礎(chǔ)上,就可以直接訪問到 p1和 p2里面的成員,換句通俗的話來將,就是說,我把你當做朋友,你就獲得了一些權(quán)限,更改的代碼如下所示:
- class Point
- {
- private:
- int x;
- int y;
- public:
- Point(){}
- Point(int x, int y) : x(x),y(y){}
- friend Point add(Point &p1, Point &p2);
- };
- Point add(Point &p1, Point &p2)
- {
- Point n;
- n.x = p1.x + p2.x;
- n.y = p2.x + p2.y;
- return n;
- }
聲明成友元之后,在函數(shù)里就可以訪問到類里面的私有數(shù)據(jù)成員,大大簡化了代碼量。
運算符重載
上述介紹友元的時候,我們將兩個實例化的對象進行相加,使用的是 C 語言的思路,但是對于 C++來說,其具備運算符重載的特性,也就是能夠重載一個+號運算符用于類的相加。為了展開這個知識點,依舊先從之前學習 C語言時的角度去看這個問題,我們之前學習 C語言的時候,我們會接觸到這樣一個概念,就是++p 和 p++,比如有如下所示的代碼:
- int a = 1;
- int b;
- b = ++a;
上述代碼的意思分解一下是這樣子的:
- int a = 1;
- int b;
- a = a + 1;
- b = a;
這樣一來,b的結(jié)果就是 2。但是如果像下面這樣子的代碼:
- int a = 1;
- int b;
- b = a++;
上面的代碼分解一下,就是下面這樣子的:
- int a = 1;
- int b;
- b = a;
- a = a++;
這樣子,運行后 b的結(jié)果是 1。
現(xiàn)在我們要來實現(xiàn)這個前 ++和后 ++的運算符重載,實現(xiàn)類里面成員的++,繼續(xù)沿用上述的代碼,基于 Point類,我們來編寫重載的函數(shù),代碼如下所示:
- Point operator++(Point &p) /* 引用節(jié)省內(nèi)存 */
- {
- p.x = p.x + 1;
- p.y = p.y + 1;
- return p;
- }
前 ++和后 ++的運算符一致,然而在重載函數(shù)中,是通過形參的不同來進行重載函數(shù)的,因此,我們在編寫后 ++的重載函數(shù)的時候,需要新增一個參數(shù),比如下面的代碼:
- Point operator++(Point &p, int a)
- {
- Point n;
- n = p;
- p.x = p.x + 1;
- p.y = p.y + 1;
- return n;
- }
上述的重載函數(shù),因為都操作了類里面的私有數(shù)據(jù)成員,因此,必須將其聲明為友元。下面是代碼實現(xiàn):
- class Point
- {
- private:
- int x;
- int y;
- public:
- Point(){}
- Point(int x, int y) : x(x), y(y){}
- friend Point operator++(Point &p);
- friend Point operator++(Point &p, int a);
- void printfInfo(void)
- {
- cout << "(" << x << "," << y << ")" << endl;
- }
- };
需要注意的一點是,上述的形參里面使用的是 p的引用,為什么要使用引用是因為引用傳入的是地址,占四個字節(jié)的大小,但是如果傳入的不是引用,那么就要占用整個類那么大的大小。這樣做也就節(jié)省了存儲空間。
緊接著,我們來編寫主函數(shù)的代碼:
- int main(int argc, char **argv)
- {
- Point p1(1, 2);
- Point p2(3, 4);
- Point n;
- n = ++p1;
- n.printfInfo();
- cout << "**********************" << endl;
- Point n2;
- n2 = p2++;
- n2.printfInfo();
- }
下面是代碼的運行結(jié)果:
通過運行結(jié)果可以知道,我們實現(xiàn)了前 ++和 后++的效果。
小結(jié)
上述便是本次教程分享的內(nèi)容,其中提到了運算符重載這一知識點還包含很多的應(yīng)用,本次只是簡單地用一個例子進行了介紹,下期教程將詳細介紹運算符重載地其他內(nèi)容,本次的分享到這里就結(jié)束咯~
本文轉(zhuǎn)載自微信公眾號「wenzi嵌入式軟件」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系wenzi嵌入式軟件公眾號。