程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++面向對象編程入門:類(class)

C++面向對象編程入門:類(class)

編輯:關於C++

上兩篇內容我們著重說了結構體相關知識的操作。

以後的內容我們將逐步完全以c++作為主體了,這也意味著我們的教程正式進入面向對象的編程了。

前面的教程我已經再三說明,結構體的掌握非常重要,重要在哪裡呢?重要在結構體和類有相同的特性,但又有很大的區別,類是構成面向對象編程的基礎,但它是和結構體有著極其密切的關系。

我們在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++的特性是

對象=算法+數據結構

程序=對象+對象+對象+對象+........

所以根據這一特性,我們在定義一個自己定義的結構體變量的時候。這個變量就應該是叫做對象或者叫實例。

例如

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

好了,到這裡關於類的知識點我們已經學習完,希望大家多多練習

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved