結構體的掌握非常重要,重要在哪裡呢?重要在結構體和類有相同的特性,但又有很大的區別,類是構成物件導向編程的基礎,但它是和結構體有著極其密切的關係。 我們在c語言中建立一個結構體我們使用如下方法:
struct test
{
private: int number;
public: float socre;
};
類的建立方式和結構體幾乎一樣,看如下的代碼:
class test
{
private: int number;
public: float socre;
public: int rp()
{
return number;
}
void setnum(int a)
{
number=a;
}
};
但是大家注意到沒有,標準c中是不允許在結構體中聲明函數的,但c++中的類可以,這一點就和c有了本質的區別,很好的體現了c++物件導向的特點!過去的c語言是一種非物件導向的語言,他的特性是:程式=演算法+資料結構。但c++的特性是:對象=演算法+資料結構 程式=對象+對象+對象+對象+........ 所以根據這一特性,我們在定義一個自己定義的結構體變數的時候。這個變數就應該是叫做對象或者叫執行個體。
例如上面的例子中的rp()成員函數,我們如果有如下定義: test a; 的話,調用rp()就應該寫成: a.rp(); 成員函數的調用和普通成員變數的調用方式一致都採用.的操作符。
這一小節為了鞏固聯絡我給出一個完整的例子。
如下(重要和特殊的地方都有詳細的註解):
#include <iostream>
using namespace std;
class test
{
private://私人成員類外不能夠直接存取 int number;
public://共有成員類外能夠直接存取 float socre;
public: int rp()
{
return number;
}
void setnum(int a)
{
number=a;
}
};
void main()
{
test a; //a.number=10;//錯誤的,私人成員不能外部存取
a.socre=99.9f;
cout<<a.socre<<endl;//公有成員可以外部存取
a.setnum(100);//通過公有成員函數setnum()間接對私人成員number進行賦值操作 cout<<a.rp();//間接返回私人成員number的值
cin.get();
}
好了,介紹了在類內部定義成員函數(方法)的方法,下面我們要介紹一下域區分符(::)的作用了。
下面我們來看一個例子,利用這個例子中我們要說明兩個重要問題:
#include <iostream>
using namespace std;
int pp=0;
class test
{
private: int number;
public: float socre; int pp;
public: void rp();
};
void test::rp()//在外部利用域區分符定義test類的成員函數
{ ::pp=11;//變數名前加域區分符給全域變數pp賦值
pp=100;//設定結構體變數
}
void main()
{
test a; test b; a.rp();
cout<<pp<<endl;
cout<<a.pp<<endl;
cin.get();
}
例如 test a; 那麼a就是test結構的一個對象(執行個體) test結構體內的成員可以叫做是分量,例如: a.socre=10.1f; 那麼number就是test結構的對象a的分量(或者叫資料成員,或者叫屬性)score;
在c語言中結構體中的各成員他們的預設儲存控制是public 而 c++中類的預設儲存控制是private,所以在類中的成員如果需要外部掉用一定要加上關鍵字public聲明成公有類型,這一特性同樣使用於類中的成員函數,函數的操作方式和普通函數差別並不大。
例如上面的例子中的rp()成員函數,我們如果有如下定義: test a; 的話,調用rp()就應該寫成: a.rp(); 成員函數的調用和普通成員變數的調用方式一致都採用.的操作符。
這一小節為了鞏固聯絡我給出一個完整的例子。
如下(重要和特殊的地方都有詳細的註解):
#include <iostream>
using namespace std;
class test
{
private://私人成員類外不能夠直接存取 int number;
public://共有成員類外能夠直接存取 float socre;
public: int rp()
{ return number; }
void setnum(int a)
{ number=a; }
};
void main()
{
test a; //a.number=10;//錯誤的,私人成員不能外部存取
a.socre=99.9f;
cout<<a.socre<<endl;//公有成員可以外部存取
a.setnum(100);//通過公有成員函數setnum()間接對私人成員number進行賦值操作
cout<<a.rp();//間接返回私人成員number的值
cin.get();
}
好了,介紹了在類內部定義成員函數(方法)的方法,下面我們要介紹一下域區分符(::)的作用了。
下面我們來看一個例子,利用這個例子中我們要說明兩個重要問題:
#include <iostream>
using namespace std;
int pp=0;
class test
{
private: int number;
public: float socre; int pp;
public: void rp(); };
void test::rp()//在外部利用域區分符定義test類的成員函數
{
::pp=11;//變數名前加域區分符給全域變數pp賦值
pp=100;//設定結構體變數
}
void main()
{
test a;
test b;
a.rp();
cout<<pp<<endl;
cout<<a.pp<<endl;
cin.get();
}
問題1:
利用域區分符我們可以在類定義的外部設定成員函數,但要注意的是,在類的內部必須預先聲明: void test::rp() 在函數類型的後面加上類的名稱再加上域區分符(::)再加函數名稱,利用這樣的方法我們就在類的外部建立了一個名為rp的test類大成員函數(方法),可能很多人要問,這麼做有意義嗎?在類的內部寫函數代碼不是更好? 答案是這樣的:在類的定義中,一般成員函數的規模一般都比較小,而且一些特殊的語句是不能夠使用的,而且一般會被自動的設定成為inline(內聯)函數,即使你沒有明確的聲明為inline,那麼為什麼有會被自動化佈建成為inline呢?因為大多數情況下,類的定義一般是放在標頭檔中的,在編譯的時候這些函數的定義也隨之進入標頭檔,這樣就會導致被多次編譯,如果是inline的情況,函數定義在調用處擴充,就避免了重複編譯的問題,而且把大量的成員函數都放在類中使用起來也十分不方便,為了避免這種情況的發生,所以c++是允許在外部定義類的成員函數(方法)的,將類定義和其它成員函數定義分開,是物件導向編程的通常做法,我們把類的定義在這裡也就是標頭檔了看作是類的外部介面,類的成員函數的定義看成是類的內部實現。寫程式的時候只需要外部介面也就是標頭檔即可,這一特點和我們使用標準庫函數的道理是一致的,因為在類的定義中,已經包含了成員函數(方法)的聲明。
問題二
域區分符和外部全域變數和類成員變數之間的關係。在上面的代碼中我們看到了,外部全域和類內部都有一個叫做pp的整形變數,那麼我們要區分操作他們用什麼方法呢?使用域區分符就可以做到這一點,在上面的代碼中::pp=11;操作的就是外部的同名稱全域變數,pp=100;操作的就是類內部的成員變數,這一點十分重要!
問題三
一個類的所有對象調用的都是同一段代碼,那麼操作成員變數的時候電腦有是如何知道哪個成員是屬於哪個對象的呢?這裡牽扯到一個隱藏的this指標的問題,上面的代碼在調用a.rp()的的時候,系統自動傳遞一了個a對象的指標給函數,在內部的時候pp=100;的時候其實就是this->pp=100; 所以你把上面的成員函數寫成如下形勢也是正確的:
void test::rp()
{
::pp=11;
this->pp=100;//this指標就是指向a對象的指標
}
類的成員函數和普通函數一樣是可以進行重載操作的,關於重載函數前面已經說過,這裡不再說明。
給出例子仔細看:
#include <iostream>
using namespace std;
class test
{
private: int number;
public: float socre; int pp;
public: void rp(int);
void rp(float);
};
void test::rp(int a)//在外部利用域區分符定義test類的成員函數
{ cout<<"調用成員函數!a:"<<a<<endl; }
void test::rp(float a)//在外部利用域區分符定義test類的成員函數
{ cout<<"調用成員函數!a:"<<a<<endl; }
void main()
{
test a;
a.rp(100);
a.rp(3.14f);
cin.get();
}
下面我們來看一下利用指標和利用引用間接調用類的成員函數,對於對於指標和引用調用成員函數和調用普通函數差別不大,在這裡也就不再重複說明了,注意看代碼,多試多練習既可。 代碼如下:
#include <iostream>
using namespace std;
class test
{
private: int number;
public: float socre; int pp;
public: int rp(int);
};
int test::rp(int a)//在外部利用域區分符定義test類的成員函數
{
number=100;
return a + number;
}
void run(test *p)//利用指標調用
{
cout<<p->rp(100)<<endl;
}
void run(test &p)//利用引用
{
cout<<p.rp(200)<<endl;
}
void main()
{
test a;
run(&a);
run(a);
cin.get();
}
前面我們說過,類的成員如果不顯式的生命為public那麼它預設的就是private就是私人的,私人聲明可以保護成員不能夠被外部存取,但在c++還有一個修飾符,它具有和private相似的效能,它就是protected修飾符。 在這裡我們簡單說明一下,他們三著之間的差別: 在類的private:節中聲明的成員(無論資料成員或是成員函數)僅僅能被類的成員函數和友元訪問。 在類的protected: 節中聲明的成員(無論資料成員或是成員函數)僅僅能被類的成員函數,友元以及子類的成員函數和友元訪問。 在類的public:節中聲明的成員(無論資料成員或是成員函數)能被任何人訪問。 由於private和protected的差別主要是體現在類的繼承中,現在的教程還沒有設計到友元和子類所以這裡不做深入討論,但上面的三點務必記得,在以後的教程中我們會回過頭來說明的。 總的來說,類成員的保護無非是為了以下四點! 1.相對與普通函數和其它類的成員函數來說,保護類的資料不能夠被肆意的篡改侵犯! 2.使類對它本身的內部資料維護負責,只有類自己才能夠訪問自己的保護資料! 3.限制類的外部介面,把一個類分成公有的和受保護的兩部分,對於使用者來說它只要會用就可以,無須瞭解內部完整結構,起到黑盒的效果。 4.減少類與其它代碼的關聯程,類的功能是獨立的,不需要依靠應用程式的運行環境,這個程式可以用它,另外一個也可以用它,使得你可以輕易的用一個類替換另一個類。 下面為了示範類成員的保護特性,我們來做一個球類遊戲! 我們設計一個類,來計算球員的平均成績,要求在外部不能夠隨意篡改球員的平均成績。
我們把該類命名為ballscore並且把它放到ballscore.h的有檔案中!
class ballscore
{
protected: const static int gbs = 5; //好球單位得分
const static int bbs = -3; //壞球單位扣分
float gradescore; //平均成績
public: float GetGS(float goodball,float badball) //goodball為好球數量,badball為壞求數量
{ gradescore = (goodball*gbs + badball*bbs) / (goodball + badball);
return gradescore; //返回平均成績
}
};
主函數調用:
#include <iostream>
#include "ballscore.h"
using namespace std;
void main()
{
ballscore jeff;
cout<<jeff.GetGS(10,3);
jeff.gradescore=5.5//想篡改jeff的平均成績是錯誤的!
cin.get();
}
在上面的代碼中標頭檔和類的使用很好了體現了類的黑盒特性,誰也不能夠在外部修改球員的平均成績!類體中的有一個地方要注意 const static int gbs = 5;//好球單位得分 const static int bbs = -3;//壞球單位扣分 ,之所以要修飾成const static 因為c++中類成員只有靜態整形的常量才能夠被初始化,到這裡整個程式也就說完了,當然真正大比賽不可能是這樣,只是為了說明問題就題命題而已,呵呵! 下面我們說一下關於類的範圍。 在說內容之前我們先給出這部分內容的一個完整代碼,
看講解的是參照此一下代碼。
#include <iostream>
using namespace std;
class ballscore
{
protected: const static int gbs = 5;//好球單位得分
const static int bbs = -3;//壞球單位扣分
float gradescore;//平均成績
public: float GetGS(float goodball,float badball) //goodball為好球數量,badball為壞求數量
{
int gradescore=0; //新定義一個和成員變數float gradescore相同名字的類成員函數局部變數
ballscore::gradescore = (goodball*gbs + badball*bbs) / (goodball + badball); //由於局部變數與類成員變數同名使用的時候必須在其前加上類名和域區分符
return ballscore::gradescore;//返回平均成績
}
};
int ballscore=0;//定義一個與類名稱相同的普通全域變數
int test;
void main()
{
class test//局部類的建立
{ float a; float b; };
test test; ::test=1; //由於局部類名隱藏了外部變數使用需加域區分符
class ballscore jeff; //由於全域變數int ballsocre和類(ballsocre)名稱相同,隱藏了類名稱,這時候定義類對象需加class首碼以區分
cout<<jeff.GetGS(10,3);
cin.get(); }
類的範圍是只指定義和相應的成員函數定義的範圍,在該範圍內,一個類的成員函數對同一類的資料成員具有無限制的訪問權。 在類的使用中,我們經常會碰到以下三種情況: 1.類的成員函數的局部變數隱藏了類的同名成員變數,看如對上面程式的分析。
protected: const static int gbs = 5;
const static int bbs = -3;
float gradescore;
public: float GetGS(float goodball,float badball)
{
int gradescore=0;
ballscore::gradescore = (goodball*gbs + badball*bbs) / (goodball + badball);
return ballscore::gradescore;
}
代碼中的int gradescore就把float gradescore給隱藏了,所以要使用成員變數float gradescore的時候必須在其之前加上類名稱和域區分符(::)。
2.在類定義外部非類型名隱藏了類型名稱的情況,看上面代碼的分析!
class ballscore
{
protected: const static int gbs = 5;
const static int bbs = -3;
float gradescore;
public: float GetGS(float goodball,float badball)
{
int gradescore=0;
ballscore::gradescore = (goodball*gbs + badball*bbs) / (goodball + badball);
return ballscore::gradescore;
}
};
int ballscore=0; 代碼中的全部變數int ballscore隱藏了類名稱class ballscore 所以在main中如如果要定義ballscore類的對象就要在類名稱前加上class關鍵字 class ballscore jeff;
3.類型名稱隱藏了非類型名稱,看對上面代碼的分析
int test;
void main()
{
class test
{ float a; float b; };
test test;
::test=1;
class ballscore jeff;
cout<<jeff.GetGS(10,3);
cin.get();
}
在普通函數內部定義的類叫做局部類,代碼中的test類就是一個局部類! 代碼中的test類隱藏了全域變數test如果要操作全域變數test那麼就要在test前加上域區分符號(::),進行使用! ::test=1就是對全域變數test進行了賦值操作。 我們最後說一下名字空間! 名字空間就是指某一個名字在其中必須是唯一的範圍. 如果這個定義想不明白,可以簡單的說成,在一個地區內,某一個名字在裡面使用必須是唯一的,不能出現重複定義的情況出現,這個地區就是名字空間! c++規定:
1.一個名字不能同時設定為兩種不同的類型 class test { //... }; typedef int test; 這個就是錯誤的!
2.非類型名(變數名,常量名,函數名,對象名,枚舉成員)不能重名. test a; void a(); 就是錯誤的,因為a是一個test類的對象,它和函數a名稱重名了!
3.類型與非類型不在同一個名字空間上,可以重名,即使在同一範圍內,但兩者同時出現時定義類對象的時候要加上首碼class以區分類型和非類型名! class test { //..... } int test class test a;//利用class前墜區分,定義了一個test類的對象a