C++之类和对象
一,C中結(jié)構(gòu)體的缺陷
1,main函數(shù)中的任意賦值語句都可以訪問結(jié)構(gòu)體中的成員,但在現(xiàn)實生活中并不是什么數(shù)據(jù)都可以被隨意訪問的,因此C語言中的結(jié)構(gòu)體的數(shù)據(jù)是不安全的;
2,結(jié)構(gòu)體中的數(shù)據(jù)和對該數(shù)據(jù)的操作是分離的,并不是一個被封裝起來的整體,因此使程序難以重用,影響了軟件生產(chǎn)效率;于是C++中引入了類的概念。
?客戶/服務(wù)模型
? OOP(面向?qū)ο蟪绦蛟O(shè)計)程序員常依照客戶/服務(wù)模型來討論程序設(shè)計。在這個概念中,客戶是使用類的程序。類聲明(包括類方法)構(gòu)成了服務(wù)器,他是程序可以使用的資源。客戶只能通過以公有方式定義的接口使用服務(wù)器,這意味著客戶程序員唯一的責(zé)任是了解該接口。服務(wù)器設(shè)計人員的責(zé)任時確保服務(wù)器根據(jù)該接口可靠并準確的執(zhí)行。服務(wù)器設(shè)計人員只能修改類設(shè)計的實現(xiàn)細節(jié),而不能修改接口。這樣程序員獨立的對接口和服務(wù)器進行改進,對服務(wù)器的修改不會對客戶的行為造成意外的影響。
?
二,類與對象
#include <iostream> using namespace std;class Point {private:static int count; //記錄構(gòu)造的對象數(shù),靜態(tài)數(shù)據(jù)成員int x_;int y_;string name_;public:Point(const string &name, int i, int j); //帶參構(gòu)造函數(shù)Point(); //默認構(gòu)造函數(shù),構(gòu)造函數(shù)重載Point(const Point &p); //拷貝構(gòu)造函數(shù)~Point(void); //析構(gòu)函數(shù)static void showCount(); //靜態(tài)成員函數(shù)void showPoint();int ReadX() const{//內(nèi)聯(lián)成員函數(shù)(隱式聲明),在聲明前面加inline修飾顯示聲明內(nèi)聯(lián)函數(shù)return x_;}; int ReadY() const {return y_; }; };//成員函數(shù)實現(xiàn)部分 Point::Point(const string & name, int x, int y) {count++;name_ = name;x_ = x;y_ = y;cout<<count<<":調(diào)用非默認構(gòu)造函數(shù):Point("<<name_<<","<<x_<<","<<y_<<")"<<endl; }Point::Point() {count++;x_ = y_ = 0; name_ = "no name";cout<<count<<":調(diào)用默認構(gòu)造函數(shù):Point("<<name_<<","<<x_<<","<<y_<<")"<<endl; }Point::Point(const Point &p) {count++;name_ = p.name_;x_ = p.x_;y_ = p.y_;cout<<count<<":調(diào)用拷貝構(gòu)造函數(shù):Point("<<name_<<","<<x_<<","<<y_<<")"<<endl; }void Point::showPoint() {cout<<"Point對象:"<<name_<<","<<x_<<","<<y_<<endl; }void Point::showCount() {cout<<"目前已經(jīng)創(chuàng)建了count="<<count<<"個對象"<<endl; }Point::~Point(void) {count--;cout<<"調(diào)用析構(gòu)函數(shù),釋放類Point("<<name_<<","<<x_<<","<<y_<<")"<<endl; }int Point::count = 0; //初始化靜態(tài)數(shù)據(jù)成員,注意使用類名限定 Point func(Point pointA);int main(void) {{Point pointA("pointA", 20, 30);Point pointC(pointA); //用pointA初始化pointC,調(diào)用拷貝構(gòu)造函數(shù)(1) pointC.showPoint();Point pointB; //pointB的數(shù)據(jù)成員被初始化為0//賦值運算,這里不是初始化.創(chuàng)建一個臨時對象然后賦值給pointBpointB = Point("pointB", 1, 2); Point pointD = Point("pointD", 3, 4); //使用臨時對象初始化pointD//Point pointE{"pointE", 5, 6};//使用初始化列表初始化pointE, C++11 Point *pointP = new Point("pointP", 7, 8); //C++11delete pointP;pointB = func(pointA);pointB.showPoint();cout<<"Done!"<<endl;}cout<<"exit now!\n";return 0; }Point func(Point pointA) //類作形參,調(diào)用拷貝構(gòu)造函數(shù)(2) {cout<<"在func函數(shù)體內(nèi)\n";return Point("temp", pointA.ReadX()+10, pointA.ReadY()+10); //定義臨時對象,調(diào)用拷貝構(gòu)造函數(shù)3次返回Point類 } View Code運行結(jié)果(VC6.0):
1:調(diào)用非默認構(gòu)造函數(shù):Point(pointA,20,30)
2:調(diào)用拷貝構(gòu)造函數(shù):Point(pointA,20,30)
Point對象:pointA,20,30
3:調(diào)用默認構(gòu)造函數(shù):Point(no name,0,0)
4:調(diào)用非默認構(gòu)造函數(shù):Point(pointB,1,2)
調(diào)用析構(gòu)函數(shù),釋放類Point(pointB,1,2)
4:調(diào)用非默認構(gòu)造函數(shù):Point(pointD,3,4)
5:調(diào)用非默認構(gòu)造函數(shù):Point(pointP,7,8)
調(diào)用析構(gòu)函數(shù),釋放類Point(pointP,7,8)
5:調(diào)用拷貝構(gòu)造函數(shù):Point(pointA,20,30)
在func函數(shù)體內(nèi)
6:調(diào)用非默認構(gòu)造函數(shù):Point(temp,30,40)
調(diào)用析構(gòu)函數(shù),釋放類Point(temp,30,40)
調(diào)用析構(gòu)函數(shù),釋放類Point(pointA,20,30)
Point對象:temp,30,40
Done!
調(diào)用析構(gòu)函數(shù),釋放類Point(pointD,3,4)
調(diào)用析構(gòu)函數(shù),釋放類Point(temp,30,40)
調(diào)用析構(gòu)函數(shù),釋放類Point(pointA,20,30)
調(diào)用析構(gòu)函數(shù),釋放類Point(pointA,20,30)
exit now!
1,類與對象
數(shù)據(jù)成員:只有數(shù)據(jù)類型,無存儲類型,因此不能再類的聲明中初始化數(shù)據(jù)成員;在類外,不能訪問私有和保護成員
成員函數(shù)在類外定義:
成員函數(shù)體內(nèi)可以訪問該類的所有數(shù)據(jù)成員;非靜態(tài)成員函數(shù)中都有一個隱含的參數(shù),即this指針,該指針指向當(dāng)前正在調(diào)用成員函數(shù)的對象
2,構(gòu)造函數(shù)與析構(gòu)函數(shù)
默認構(gòu)造函數(shù):當(dāng)且僅當(dāng)沒有定義任何構(gòu)造函數(shù)時,編譯器才會提供默認構(gòu)造函數(shù)。所以當(dāng)定義非默認的構(gòu)造函數(shù)時,必須定義默認構(gòu)造函數(shù),否則下面的聲明將會出錯:
?????????????????????? Point pointB; //調(diào)用默認構(gòu)造函數(shù)
? 或者是給非默認構(gòu)造函數(shù)提供默認值:Point(const string &name = "no name", int x=0, int y=0);
析構(gòu)函數(shù):無參數(shù)不可重載,在對象存在的函數(shù)體結(jié)束時或使用delete釋放new創(chuàng)建的對象時被自動調(diào)用
缺省拷貝構(gòu)造函數(shù):把初始對象的每個數(shù)據(jù)成員的值都復(fù)制到新建對象中
它們都沒有返回值;調(diào)用析構(gòu)函數(shù)和構(gòu)造函數(shù)的順序正好相反;對沒有定義構(gòu)造函數(shù)的類,其公有數(shù)據(jù)成員可以用初始化值表進行初始化
拷貝構(gòu)造函數(shù)的調(diào)用:
a,由一個類的對象M初始化該類另一個對象N時:Point N(M);
b,當(dāng)類的對象N作為函數(shù)實參傳遞給函數(shù)形參時:P = func(N);
3,類的作用域
定義成員函數(shù)時,使用作用域運算符(::)來標識所屬的類。
類的作用域:類作用域意味著不能不能從外部直接訪問類的成員,公有成員函數(shù)也是如此。在類聲明或成員函數(shù)定義中,可以使用未限定的成員名稱 。在其他情況下使用類成員名時,必須根據(jù)上下文使用直接成員運算符(.),間接成員運算符(->)或作用域解析運算符(::)。
作用域為類的常量:
class Bakery { private:static const int Months = 12; //該常量與其他靜態(tài)變量存儲在一起,而不是存儲在對象中double costs[Months]; };//或者定義枚舉常量 class Bakery { private:enum {Months = 12};double costs[Months]; };?
作用域內(nèi)枚舉:
當(dāng)兩個枚舉定義中的枚舉量可能發(fā)生沖突時,可以定義作用域為類的枚舉。如下所示:
//發(fā)生沖突的枚舉定義 enum egg {Small, Medium, Large, Jumbo}; enum t_shirt {Small, Medium, Large, Xlarge};//類作用域的枚舉定義 enum calss egg {Small, Medium, Large, Jumbo}; enum class t_shirt {Small, Medium, Large, Xlarge};enum egg choice = egg::Large; enum t_shirt Floyd = t_shirt::Large;C++11還提供了作用域內(nèi)枚舉的類型安全。在有些情況下,常量枚舉將自動轉(zhuǎn)換為整形,但作用域內(nèi)枚舉不能隱式的轉(zhuǎn)換為整形。
但在必要時可以進行顯示類型轉(zhuǎn)換:int Floyd = int(t_shirt::Large);
?
?
3,注意事項
a,定義類的指針時,不調(diào)用類的構(gòu)造函數(shù);拷貝構(gòu)造函數(shù)也是構(gòu)造函數(shù)的重載
b,malloc/free和new/delete(C++)
調(diào)用malloc和free沒有調(diào)用構(gòu)造和析構(gòu)函數(shù),而調(diào)用new和delete給類指針分配存儲空間可以執(zhí)行構(gòu)造和析構(gòu)函數(shù),也可以為其他類型的指針分配存儲空間。必須配對使用。
c,"類名 對象名",當(dāng)定義了構(gòu)造函數(shù),調(diào)用的是無參的構(gòu)造函數(shù)創(chuàng)建對象或者是帶默認形參的構(gòu)造函數(shù),沒有定義構(gòu)造函數(shù)時,調(diào)用的是缺省構(gòu)造函數(shù)。而"類名 對象名()" 不能調(diào)用任何一種構(gòu)造函數(shù)。
d,使用inline定義的內(nèi)聯(lián)函數(shù)必須將類的聲明和內(nèi)聯(lián)成員函數(shù)的定義都放在同一個文件中,否則編譯時無法進行代碼的置換
e,this指針的作用:C++的編譯系統(tǒng)用一段空間來存放各個對象共同的函數(shù)代碼段,每個對象的存儲空間只是對象數(shù)據(jù)成員所占用的存儲空間。而成員函數(shù)就是通過this指針知道是哪個對象調(diào)用了
?
三,共享機制一(靜態(tài)成員)
靜態(tài)成員實現(xiàn)一個類多個對象之間的數(shù)據(jù)共享
1,靜態(tài)數(shù)據(jù)成員
是類的所有對象共享的成員,靜態(tài)數(shù)據(jù)成員的值對每一個對象是一樣的,可以被該類的任何一個對象更新。靜態(tài)數(shù)據(jù)成員在聲明類的時候就分配了內(nèi)存,在定義對象之前就存在了。
必須在類外初始化:?
<數(shù)據(jù)類型> <類名>::<靜態(tài)數(shù)據(jù)成員名> = <值>?//一般放在類定義之后
類外對公有靜態(tài)數(shù)據(jù)成員的引用:?
??<類名>::<公有靜態(tài)成員名> 或 <對象名>.<公有靜態(tài)成員名>
2,靜態(tài)成員函數(shù)
能直接引用類中的靜態(tài)成員,但只能通過對象,對象的指針或引用來訪問類中的非靜態(tài)成員(類的對象做函數(shù)參數(shù));靜態(tài)成員函數(shù)中沒有this指針
使用格式:?
<類名>::<公有靜態(tài)成員函數(shù)名>(<參數(shù)表>) 或 <對象名>.<公有靜態(tài)成員函數(shù)名>(<參數(shù)表>)
?
四,類的組合指一個類內(nèi)嵌其他類的的對象作為成員的情況,兩個類間是包含與被包含的關(guān)系
創(chuàng)建對象時,內(nèi)嵌的對象成員會被自動創(chuàng)建,因此要初始化本類的基本數(shù)據(jù)成員和內(nèi)嵌的對象成員
組合類構(gòu)造函數(shù)(需通過初始化列表對內(nèi)嵌對象初始化): ? ? ?
類名::類名(形參表):內(nèi)嵌對象1(形參表1),內(nèi)嵌對象2(形參表2),…… {類的初始化}
#include <iostream> using namespace std; #include <cmath> class Point { private:int x,y; public:Point(int i, int j):x(i),y(j) { cout<<"調(diào)用Point構(gòu)造函數(shù):Point("<<x<<", "<<y<<")"<<endl; }Point(const Point &p) { cout<<"調(diào)用Point拷貝構(gòu)造函數(shù)"<<endl; x=p.x; y=p.y; }int ReadX() { return x; }int ReadY() { return y; }~Point() { cout<<"調(diào)用Point析構(gòu)函數(shù)"<<endl; } };class Line { public:Line(Point xp1, Point xp2); Line(int i, int j, int m, int n); //構(gòu)造函數(shù)重載~Line() { cout<<"調(diào)用Line析構(gòu)函數(shù)"<<endl; } private:Point p1, p2; };Line::Line(Point xp1, Point xp2):p1(xp1),p2(xp2) {cout<<"調(diào)用Line構(gòu)造函數(shù)1: ";cout<<"Point("<<p1.ReadX()<<", "<<p1.ReadY()<<") Point("<<p2.ReadX()<<", "<<p2.ReadY()<<")"<<endl; }Line::Line(int i, int j, int m, int n):p1(i,j),p2(m,n) {cout<<"調(diào)用Line構(gòu)造函數(shù)2: ";cout<<"Point("<<p1.ReadX()<<", "<<p1.ReadY()<<") Point("<<p2.ReadX()<<", "<<p2.ReadY()<<")"<<endl; }void main() {Line line1(Point(1,2), Point(3,4)); //用臨時對象做實參Line line2(5,6,7,8); } View Code運行結(jié)果(VC++6.0):
注意事項:
<1>自身類的對象不可以作為該類的成員;自身類的引用或指針可以作為該類的成員(遞歸類)
<2>前向引用聲明
class N; class M {private: N *n;}; //n是N類的指針 class N {public: void f(M m);}; //m是M類的對象?
五,共享機制二(友元函數(shù)和友元類)
友元提供了不同類或?qū)ο蟮某蓡T函數(shù)之間、類的成員與一般函數(shù)之間進行數(shù)據(jù)共享的機制。
<1>友元函數(shù)
在友元函數(shù)體中可以通過對象名訪問類的私有和保護成員。友元函數(shù)分友元非成員函數(shù)和友元成員函數(shù)。
友元非成員函數(shù)
友元函數(shù)為非成員函數(shù),那么在定義該函數(shù)時不用在前面加"類名::",同樣的它也沒有所謂的this指針
#include <iostream> using namespace std;class Employee { private:char *name;char *id;static int count; public:Employee(char* name, char* id);~Employee();static void showEmployeeCount();friend void showEmployee(Employee &emp); };Employee::Employee(char* name, char* id) {this->count ++;this->name = new char[strlen(name)+1];this->id = new char[strlen(id)+1];strcpy(this->name, name);strcpy(this->id, id); }void Employee::showEmployeeCount() { cout<<"雇員總數(shù): count="<<count<<endl; }void showEmployee(Employee &emp) //友元非成員函數(shù)定義 {cout<<"name="<<emp.name<<", id="<<emp.id<<endl; }Employee::~Employee() {delete []name;delete []id; }int Employee::count = 0;void main() {Employee emp[3] = { //創(chuàng)建臨時對象初始化對象數(shù)組Employee("haha", "11070000"),Employee("hehe", "11070001"),Employee("yiyi", "11070002")};for (int i=0; i<3; i++){showEmployee(emp[i]); //友元非成員函數(shù)的調(diào)用 }emp->showEmployeeCount(); } View Code運行結(jié)果(VC6.0):
友元成員函數(shù)
友元成員函數(shù)不僅可以訪問自己所在類對象中的私有,公有或保護成員,也可以訪問friend聲明語句所在的類對象中的所有成員,這樣就實現(xiàn)了類與類之前的協(xié)作
#include <iostream> using namespace std; #include <string>class Salary; //前向引用聲明class Employee { private:string name;string id;static int count; public:Employee(string name, string id);static void showEmployeeCount();void showEmployee(Salary &sal); //成員函數(shù),參數(shù)是Salary對象的引用 };Employee::Employee(string name, string id):name(name),id(id) {this->count ++; }void Employee::showEmployeeCount() { cout<<"雇員總數(shù): count="<<count<<endl; }int Employee::count = 0;class Salary { private:double salary; //薪水double wage; //工資double bonus; //獎金double commission; //提成 public:Salary(double salary, double wage, double bonus, double commission);friend void Employee::showEmployee(Salary &sal); };Salary::Salary(double salary, double wage, double bonus, double commission):salary(salary),wage(wage),bonus(bonus),commission(commission) {}void Employee::showEmployee(Salary &sal) //友元非成員函數(shù)定義 {cout<<"name="<<name<<", id="<<id<<endl;cout<<"salary: "<<sal.salary<<endl; //可以訪問Salary類中的私有數(shù)據(jù)成員cout<<"wage: "<<sal.wage<<endl;cout<<"bonus: "<<sal.bonus<<endl;cout<<"commission: "<<sal.commission<<endl; }void main() {Employee emp("yiyi", "11070002");Salary sal(10000,50000,30000,20000);emp.showEmployee(sal);emp.showEmployeeCount(); } View Code運行結(jié)果:
注意,在涉及到這種類的前向引用聲明的,將成員函數(shù)Employee::showEmployee(Salary &sal)的實現(xiàn)放在這兩個類的定義之后.要不然編譯時會出錯
<2>友元類
如果一個類被說明為另一個類的友元類,那么這個類的所有成員函數(shù)都將成為另一個類的友元函數(shù)。
注意:友元關(guān)系是單向的,不具有交換性,也不具有傳遞性。比如類A為類B的友元類,類B為類C的友元類,并不代表類A為類C的友元類,是不是友元類,看其類A有沒有在類C中聲明;
?
六,C++的多文件程序
C++的源程序基本上由3個部分組成:類的聲明部分、類的實現(xiàn)部分和類的使用部分,針對3個部分,C++中對應(yīng)分為3個文件:類聲明文件(*.h文件)、類的實現(xiàn)文件(*.cpp)和類的使用文件(*.cpp,主函數(shù)main文件)。
分為3部分主要有以下幾個方面:(1)類的實現(xiàn)文件通常會比較大,將類的聲明和實現(xiàn)放在一起,不利于程序的閱讀、管理和維護。我們通常說接口應(yīng)該和實現(xiàn)的部分分離,這樣可以更易于修改程序。(2)把類成員函數(shù)的實現(xiàn)放在聲明文件中和單獨放實現(xiàn)文件里,在編譯時是不一樣的。前者是作為類的內(nèi)聯(lián)函數(shù)來處理的。(3)對于軟件的廠商來說,它只需向用戶提供程序公開的接口(只需提供*.h文件),而不用公開程序源代碼。
?
轉(zhuǎn)載于:https://www.cnblogs.com/zhoutian220/p/4032050.html
總結(jié)
- 上一篇: Copy List with Rando
- 下一篇: LightOJ - 1422 (区间DP