C++类和对象学习总结
生活随笔
收集整理的這篇文章主要介紹了
C++类和对象学习总结
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
C++中的類(lèi)與對(duì)象
http://blog.csdn.net/qq_32583189/article/details/52412369C++中的類(lèi)與對(duì)象
C中的類(lèi)與對(duì)象
定義類(lèi)
創(chuàng)建頭文件
類(lèi)和結(jié)構(gòu)體
訪(fǎng)問(wèn)控制
作用域解析運(yùn)算符
實(shí)現(xiàn)類(lèi)成員
默認(rèn)的內(nèi)聯(lián)方法
創(chuàng)建對(duì)象
開(kāi)發(fā)環(huán)境:Visual Studio 2010
定義類(lèi)
通常,C++程序?qū)⒔涌?類(lèi)定義)放在頭文件中,并將實(shí)現(xiàn)(類(lèi)方法的代碼放在源代碼文件中),本文章以一個(gè)學(xué)生的例子來(lái)完成代碼。
學(xué)生定義:記錄三門(mén)課程(語(yǔ)文,英語(yǔ),數(shù)學(xué))的成績(jī),能夠查看學(xué)生的平均成績(jī)
創(chuàng)建頭文件
現(xiàn)在創(chuàng)建名為student.h的頭文件
//student.h -- Student class interface //version 00 #ifndef STUDENT_H_ #define STUDENT_H_#include <string>class Student{ //class declaration private:std::string name;int ch;int en;int math;float average;void count(){average = (ch + en + math+0.0F)/3;} public:Student();//構(gòu)造函數(shù)~Student();//析構(gòu)函數(shù)void setName(std::string name);void setChScore(int score);void setEnScore(int score);void setMathScore(int score);void show(); };#endif
注意:類(lèi)定義和結(jié)構(gòu)體定義一樣,要在最后加上;(分號(hào))
類(lèi)和結(jié)構(gòu)體
關(guān)鍵字class指明這些代碼定義了一個(gè)類(lèi)設(shè)計(jì),在這里class和typename不是同義詞,不能使用typename代替class
類(lèi)描述看上去很像是包含成員函數(shù)以及public和private可見(jiàn)性標(biāo)簽的結(jié)構(gòu)聲明。實(shí)際上,C++對(duì)結(jié)構(gòu)進(jìn)行了擴(kuò)展,使之具有相同的特性。它們之間唯一的區(qū)別就是,結(jié)構(gòu)體的默認(rèn)訪(fǎng)問(wèn)類(lèi)型是public,而類(lèi)為private。C++程序猿通常使用類(lèi)來(lái)實(shí)現(xiàn)類(lèi)的描述,而把結(jié)構(gòu)體作為純粹的數(shù)據(jù)對(duì)象表示。
訪(fǎng)問(wèn)控制
在C++程序中通常使用訪(fǎng)問(wèn)控制關(guān)鍵字來(lái)保證自身屬性的可見(jiàn)性。常見(jiàn)的三個(gè)關(guān)鍵字為private,public,protected
關(guān)鍵字 含義
private C++中的默認(rèn)訪(fǎng)問(wèn)權(quán)限,也就是說(shuō)上述student.h文件中的private關(guān)鍵字可以省略。其含義表示為只有該對(duì)象自身可以訪(fǎng)問(wèn)這些屬性或者方法,被private修飾的屬性和方法對(duì)于外部來(lái)說(shuō)不可見(jiàn)
public 公有權(quán)限,表示被public修飾的屬性和方法可以被外部程序直接調(diào)用
protected 受保護(hù)的權(quán)限,表示這些屬性和方法僅僅能被該類(lèi)和派生類(lèi)的內(nèi)部訪(fǎng)問(wèn),也就是說(shuō),對(duì)于外部世界來(lái)說(shuō),保護(hù)成員的行為與private相同,對(duì)于派生類(lèi)來(lái)說(shuō),保護(hù)成員的行為和public相同
作用域解析運(yùn)算符
在具體實(shí)現(xiàn)一個(gè)類(lèi)之前,我們需要先了解一個(gè)運(yùn)算符:作用域解析運(yùn)算符(::)。該符號(hào)的作用是用來(lái)標(biāo)識(shí)一個(gè)函數(shù)所屬的類(lèi),例如當(dāng)我們輸入Student::average()時(shí)。表示我們?cè)L問(wèn)的average()方法屬于Student類(lèi)而不是其他的類(lèi)。?
但是在一個(gè)Student類(lèi)的內(nèi)部我們并不需要使用使用作用域解析運(yùn)算符就能訪(fǎng)問(wèn)average()方法,這是因?yàn)樵陬?lèi)的內(nèi)部自身所定義的所有方法對(duì)自身來(lái)說(shuō)都是可見(jiàn)的。?
對(duì)于一個(gè)方法名稱(chēng)來(lái)說(shuō),Student::average()是方法的函數(shù)限定名(全稱(chēng)),而average()方法屬于非限定名(簡(jiǎn)稱(chēng)),當(dāng)程序?qū)τ谝粋€(gè)函數(shù)來(lái)源無(wú)異議時(shí)(沒(méi)有重名方法),可以使用簡(jiǎn)稱(chēng)。
實(shí)現(xiàn)類(lèi)成員
現(xiàn)在我們創(chuàng)建一個(gè)C++文件student.cpp
//student.cpp -- implementing the Student class
//version 00
#include "stdafx.h"
#include "student.h"
Student::Student(){
}
Student::~Student(){
}
void Student::setChScore(int score){
? ? Student::ch = score;
}
void Student::setName(std::string n){
? ? Student::name = n;
}
void Student::setEnScore(int score){
? ? en = score;
}
void Student::setMathScore(int score){
? ? math = score;
}
void Student::show(){
? ? Student::count();
? ? ios_base::fmtflags orig = cout.setf(ios_base::fixed,ios_base::floatfield);
? ? std::streamsize prec = cout.precision(1);
? ? cout<<name<<" 同學(xué)的語(yǔ)文成績(jī)?yōu)?#34;<<ch<<"分,數(shù)學(xué)成績(jī)?yōu)?#34;<<math<<"分,英語(yǔ)成績(jī)?yōu)?#34;<<en<<"分,平均成績(jī)"<<average<<"分"<<endl;
? ? cout.setf(orig,ios_base::floatfield);
}
注意這個(gè)實(shí)現(xiàn)的Student::setChScore(int score)中我們?cè)谑褂胏h參數(shù)時(shí)使用了作用于解析運(yùn)算符,其實(shí)在類(lèi)的內(nèi)部我們可以直接使用簡(jiǎn)稱(chēng),所以在后續(xù)的方法中我們使用了非限定名。
默認(rèn)的內(nèi)聯(lián)方法
我們?cè)陬?lèi)的定義中聲明并定義了一個(gè)count()方法,由于其位于聲明中,編譯器將其編譯為內(nèi)聯(lián)方法。一般內(nèi)容簡(jiǎn)單的方法都可以作為內(nèi)聯(lián)方法。如果我們并不想直接在定義中聲明并定義一個(gè)內(nèi)聯(lián)方法,我們也可以在實(shí)現(xiàn)時(shí)給方法添加inline關(guān)鍵字。比如
? ? inline void count(){
? ? ? ? average = (ch + en + math+0.0F)/3;
? ? }
但是我們?cè)诼暶魍舛x內(nèi)聯(lián)函數(shù)時(shí)必須注意C++中的一個(gè)規(guī)則:要求內(nèi)聯(lián)函數(shù)在每一個(gè)使用它的源文件中均由定義。也就是說(shuō)我們需要在每一個(gè)使用該方法的文件中定義該方法,很明顯我們并不想這樣做,所以最好的實(shí)現(xiàn)就是在聲明中實(shí)現(xiàn)定義方法。
創(chuàng)建對(duì)象
當(dāng)我們?cè)谑褂靡粋€(gè)類(lèi)的實(shí)例對(duì)象時(shí)需要用到一個(gè)關(guān)鍵字new,這個(gè)關(guān)鍵字和C語(yǔ)言中的malloc關(guān)鍵字類(lèi)似,都是用來(lái)在堆內(nèi)存中為數(shù)據(jù)分配一段空間。?
我們?cè)趍ain方法中舉個(gè)例子
//visual studio 2010 -- main program
#include "stdafx.h"
#include "student.h"
int _tmain(int argc, _TCHAR* argv[]){
? ? Student *jack = new Student();
? ? jack->setName("jack");
? ? jack->setChScore(98);
? ? jack->setMathScore(100);
? ? jack->setEnScore(92);
? ? jack->show();
? ? delete jack;
? ? return 0;
}
輸出結(jié)果:
jack 同學(xué)的語(yǔ)文成績(jī)?yōu)?8分,數(shù)學(xué)成績(jī)?yōu)?00分,英語(yǔ)成績(jī)?yōu)?2分,平均成績(jī)96.7分
請(qǐng)按任意鍵繼續(xù). . .
溫馨提醒:記得養(yǎng)成良好習(xí)慣,在對(duì)象不用后要使用delete關(guān)鍵字銷(xiāo)毀對(duì)象。
========
C++類(lèi)的定義和對(duì)象的創(chuàng)建
http://c.biancheng.net/cpp/biancheng/view/185.html類(lèi)和對(duì)象是 C++ 的重要特性,它們使得 C++ 成為面向?qū)ο蟮木幊陶Z(yǔ)言,可以用來(lái)開(kāi)發(fā)中大型項(xiàng)目,本節(jié)重點(diǎn)講解類(lèi)和對(duì)象的語(yǔ)法,如果你對(duì)它們的概念還不了解,請(qǐng)先閱讀《C++類(lèi)和對(duì)象的概念》。
類(lèi)是創(chuàng)建對(duì)象的模板,一個(gè)類(lèi)可以創(chuàng)建多個(gè)對(duì)象,每個(gè)對(duì)象都是類(lèi)類(lèi)型的一個(gè)變量;創(chuàng)建對(duì)象的過(guò)程也叫類(lèi)的實(shí)例化。每個(gè)對(duì)象都是類(lèi)的一個(gè)具體實(shí)例(Instance),擁有類(lèi)的成員變量和成員函數(shù)。
有些教程將類(lèi)的成員變量稱(chēng)為類(lèi)的屬性(Property),將類(lèi)的成員函數(shù)稱(chēng)為類(lèi)的方法(Method)。在面向?qū)ο蟮木幊陶Z(yǔ)言中,經(jīng)常把函數(shù)(Function)稱(chēng)為方法(Method)。
與結(jié)構(gòu)體一樣,類(lèi)只是一種復(fù)雜數(shù)據(jù)類(lèi)型的聲明,不占用內(nèi)存空間。而對(duì)象是類(lèi)這種數(shù)據(jù)類(lèi)型的一個(gè)變量,或者說(shuō)是通過(guò)類(lèi)這種數(shù)據(jù)類(lèi)型創(chuàng)建出來(lái)的一份實(shí)實(shí)在在的數(shù)據(jù),所以占用內(nèi)存空間。
類(lèi)的定義
類(lèi)是用戶(hù)自定義的類(lèi)型,如果程序中要用到類(lèi),必須提前說(shuō)明,或者使用已存在的類(lèi)(別人寫(xiě)好的類(lèi)、標(biāo)準(zhǔn)庫(kù)中的類(lèi)等),C++語(yǔ)法本身并不提供現(xiàn)成的類(lèi)的名稱(chēng)、結(jié)構(gòu)和內(nèi)容。
一個(gè)簡(jiǎn)單的類(lèi)的定義:
純文本復(fù)制
class Student{
public:
? ? //成員變量
? ? char *name;
? ? int age;
? ? float score;
? ? //成員函數(shù)
? ? void say(){
? ? ? ? cout<<name<<"的年齡是"<<age<<",成績(jī)是"<<score<<endl;
? ? }
};
class是 C++ 中新增的關(guān)鍵字,專(zhuān)門(mén)用來(lái)定義類(lèi)。Student是類(lèi)的名稱(chēng);類(lèi)名的首字母一般大寫(xiě),以和其他的標(biāo)識(shí)符區(qū)分開(kāi)。{ }內(nèi)部是類(lèi)所包含的成員變量和成員函數(shù),它們統(tǒng)稱(chēng)為類(lèi)的成員(Member);由{ }包圍起來(lái)的部分有時(shí)也稱(chēng)為類(lèi)體,和函數(shù)體的概念類(lèi)似。public也是 C++ 的新增關(guān)鍵字,它只能用在類(lèi)的定義中,表示類(lèi)的成員變量或成員函數(shù)具有“公開(kāi)”的訪(fǎng)問(wèn)權(quán)限,初學(xué)者請(qǐng)先忽略該關(guān)鍵字,我們將在《C++類(lèi)成員的訪(fǎng)問(wèn)權(quán)限》中講解。
注意在類(lèi)定義的最后有一個(gè)分號(hào);,它是類(lèi)定義的一部分,表示類(lèi)定義結(jié)束了,不能省略。
整體上講,上面的代碼創(chuàng)建了一個(gè) Student 類(lèi),它包含了 3 個(gè)成員變量和 1 個(gè)成員函數(shù)。
類(lèi)只是一個(gè)模板(Template),編譯后不占用內(nèi)存空間,所以在定義類(lèi)時(shí)不能對(duì)成員變量進(jìn)行初始化,因?yàn)闆](méi)有地方存儲(chǔ)數(shù)據(jù)。只有在創(chuàng)建對(duì)象以后才會(huì)給成員變量分配內(nèi)存,這個(gè)時(shí)候就可以賦值了。
類(lèi)可以理解為一種新的數(shù)據(jù)類(lèi)型,該數(shù)據(jù)類(lèi)型的名稱(chēng)是 Student。與 char、int、float 等基本數(shù)據(jù)類(lèi)型不同的是,Student 是一種復(fù)雜數(shù)據(jù)類(lèi)型,可以包含基本類(lèi)型,而且還有很多基本類(lèi)型中沒(méi)有的特性,以后大家會(huì)見(jiàn)到。
創(chuàng)建對(duì)象
有了 Student 類(lèi)后,就可以通過(guò)它來(lái)創(chuàng)建對(duì)象了,例如:
Student liLei; ?//創(chuàng)建對(duì)象
Student是類(lèi)名,liLei是對(duì)象名。這和使用基本類(lèi)型定義變量的形式類(lèi)似:
int a; ?//定義整型變量
從這個(gè)角度考慮,我們可以把 Student 看做一種新的數(shù)據(jù)類(lèi)型,把 liLei 看做一個(gè)變量。
在創(chuàng)建對(duì)象時(shí),class 關(guān)鍵字可要可不要,但是出于習(xí)慣我們通常會(huì)省略掉 class 關(guān)鍵字,例如:
class Student LiLei; ?//正確
Student LiLei; ?//同樣正確
除了創(chuàng)建單個(gè)對(duì)象,還可以創(chuàng)建對(duì)象數(shù)組:
Student allStu[100];
該語(yǔ)句創(chuàng)建了一個(gè) allStu 數(shù)組,它擁有100個(gè)元素,每個(gè)元素都是 Student 類(lèi)型的對(duì)象。
訪(fǎng)問(wèn)類(lèi)的成員
創(chuàng)建對(duì)象以后,可以使用點(diǎn)號(hào).來(lái)訪(fǎng)問(wèn)成員變量和成員函數(shù),這和通過(guò)結(jié)構(gòu)體變量來(lái)訪(fǎng)問(wèn)它的成員類(lèi)似,如下所示:
#include <iostream>
using namespace std;
//類(lèi)通常定義在函數(shù)外面
class Student{
public:
? ? //類(lèi)包含的變量
? ? char *name;
? ? int age;
? ? float score;
? ? //類(lèi)包含的函數(shù)
? ? void say(){
? ? ? ? cout<<name<<"的年齡是"<<age<<",成績(jī)是"<<score<<endl;
? ? }
};
int main(){
? ? //創(chuàng)建對(duì)象
? ? Student stu;
? ? stu.name = "小明";
? ? stu.age = 15;
? ? stu.score = 92.5f;
? ? stu.say();
? ? return 0;
}
運(yùn)行結(jié)果:
小明的年齡是15,成績(jī)是92.5
stu 是一個(gè)對(duì)象,占用內(nèi)存空間,可以對(duì)它的成員變量賦值,也可以讀取它的成員變量。
類(lèi)通常定義在函數(shù)外面,當(dāng)然也可以定義在函數(shù)內(nèi)部,不過(guò)很少這樣使用。
使用對(duì)象指針
C語(yǔ)言中經(jīng)典的指針在 C++ 中仍然廣泛使用,尤其是指向?qū)ο蟮闹羔?#xff0c;沒(méi)有它就不能實(shí)現(xiàn)某些功能。
上面代碼中創(chuàng)建的對(duì)象 stu 在棧上分配內(nèi)存,需要使用&獲取它的地址,例如:
Student stu;
Student *pStu = &stu;
pStu 是一個(gè)指針,它指向 Student 類(lèi)型的數(shù)據(jù),也就是通過(guò) Student 創(chuàng)建出來(lái)的對(duì)象。
當(dāng)然,你也可以在堆上創(chuàng)建對(duì)象,這個(gè)時(shí)候就需要使用前面講到的new關(guān)鍵字,例如:
Student *pStu = new Student;
在棧上創(chuàng)建出來(lái)的對(duì)象都有一個(gè)名字,比如 stu,使用指針指向它不是必須的。但是通過(guò) new 創(chuàng)建出來(lái)的對(duì)象就不一樣了,它在堆上分配內(nèi)存,沒(méi)有名字,只能得到一個(gè)指向它的指針,所以必須使用一個(gè)指針變量來(lái)接收這個(gè)指針,否則以后再也無(wú)法找到這個(gè)對(duì)象了,更沒(méi)有辦法使用它。也就是說(shuō),使用 new 在堆上創(chuàng)建出來(lái)的對(duì)象是匿名的,沒(méi)法直接使用,必須要用一個(gè)指針指向它,再借助指針來(lái)訪(fǎng)問(wèn)它的成員變量或成員函數(shù)。
棧內(nèi)存是程序自動(dòng)管理的,不能使用 delete 刪除在棧上創(chuàng)建的對(duì)象;堆內(nèi)存由程序員管理,對(duì)象使用完畢后可以通過(guò) delete 刪除。在實(shí)際開(kāi)發(fā)中,new 和 delete 往往成對(duì)出現(xiàn),以保證及時(shí)刪除不再使用的對(duì)象,防止無(wú)用內(nèi)存堆積。
棧(Stack)和堆(Heap)是 C/C++ 程序員必須要了解的兩個(gè)概念,我們已在《C語(yǔ)言和內(nèi)存》專(zhuān)題中進(jìn)行了深入講解,相信你必將有所頓悟。
有了對(duì)象指針后,可以通過(guò)箭頭->來(lái)訪(fǎng)問(wèn)對(duì)象的成員變量和成員函數(shù),這和通過(guò)結(jié)構(gòu)體指針來(lái)訪(fǎng)問(wèn)它的成員類(lèi)似,請(qǐng)看下面的示例:
pStu -> name = "小明";
pStu -> age = 15;
pStu -> score = 92.5f;
pStu -> say();
下面是一個(gè)完整的例子:
#include <iostream>
using namespace std;
class Student{
public:
? ? char *name;
? ? int age;
? ? float score;
? ? void say(){
? ? ? ? cout<<name<<"的年齡是"<<age<<",成績(jī)是"<<score<<endl;
? ? }
};
int main(){
? ? Student *pStu = new Student;
? ? pStu -> name = "小明";
? ? pStu -> age = 15;
? ? pStu -> score = 92.5f;
? ? pStu -> say();
? ? delete pStu; ?//刪除對(duì)象
? ? return 0;
}
運(yùn)行結(jié)果:
小明的年齡是15,成績(jī)是92.5
雖然在一般的程序中無(wú)視垃圾內(nèi)存影響不大,但記得 delete 掉不再使用的對(duì)象依然是一種良好的編程習(xí)慣。
總結(jié)
本節(jié)重點(diǎn)講解了兩種創(chuàng)建對(duì)象的方式:一種是在棧上創(chuàng)建,形式和定義普通變量類(lèi)似;另外一種是在堆上創(chuàng)建,必須要用一個(gè)指針指向它,讀者要記得 delete 掉不再使用的對(duì)象。
通過(guò)對(duì)象名字訪(fǎng)問(wèn)成員使用點(diǎn)號(hào).,通過(guò)對(duì)象指針訪(fǎng)問(wèn)成員使用箭頭->,這和結(jié)構(gòu)體非常類(lèi)似。
========
C++類(lèi)(Class)總結(jié)
http://www.cnblogs.com/xiongxuanwen/p/4290086.html?
一、C++類(lèi)的定義
? ? C++中使用關(guān)鍵字 class 來(lái)定義類(lèi), 其基本形式如下:
class 類(lèi)名
{
public:
//行為或?qū)傩?
protected:
//行為或?qū)傩?
private:
//行為或?qū)傩?
};
?
示例:
? ? ?定義一個(gè)點(diǎn)(Point)類(lèi), 具有以下屬性和方法:
? ? ? 屬性: x坐標(biāo), y坐標(biāo)
? ? ? 方法: 1.設(shè)置x,y的坐標(biāo)值; 2.輸出坐標(biāo)的信息。
實(shí)現(xiàn)代碼:
class Point
{
public:
? ? ?void setPoint(int x, int y);
? ? ?void printPoint();
?
private:
? ? ?int xPos;
? ? ?int yPos;
}; ?
代碼說(shuō)明:
? ? ?上段代碼中定義了一個(gè)名為 Point 的類(lèi), 具有兩個(gè)私密屬性, int型的xPos和yPos, 分別用來(lái)表示x點(diǎn)和y點(diǎn)。
? ? ?在方法上, setPoint 用來(lái)設(shè)置屬性, 也就是 xPos 和 yPos 的值; printPoint 用來(lái)輸出點(diǎn)的信息。 ? ?
1 數(shù)據(jù)抽象和封裝
? ? ?抽象是通過(guò)特定的實(shí)例抽取共同特征以后形成概念的過(guò)程。一個(gè)對(duì)象是現(xiàn)實(shí)世界中一個(gè)實(shí)體的抽象,一個(gè)類(lèi)是一組對(duì)象的抽象。
? ? ?封裝是將相關(guān)的概念組成一個(gè)單元,然后通過(guò)一個(gè)名稱(chēng)來(lái)引用它。面向?qū)ο蠓庋b是將數(shù)據(jù)和基于數(shù)據(jù)的操作封裝成一個(gè)整體對(duì)象,對(duì)數(shù)據(jù)的訪(fǎng)問(wèn)或修改只能通過(guò)對(duì)象對(duì)外提供的接口進(jìn)行。
?
2 類(lèi)定義
? ? ?幾個(gè)重要名詞:
(1) 類(lèi)名
? ? ?遵循一般的命名規(guī)則; 字母,數(shù)字和下劃線(xiàn)組合,不要以數(shù)字開(kāi)頭。
(2) 類(lèi)成員
? ? ?類(lèi)可以沒(méi)有成員,也可以定義多個(gè)成員。成員可以是數(shù)據(jù)、函數(shù)或類(lèi)型別名。所有的成員都必須在類(lèi)的內(nèi)部聲明。
? ? ?沒(méi)有成員的類(lèi)是空類(lèi),空類(lèi)也占用空間。
class People
{
};
sizeof(People) = 1; ? ?
(3) 構(gòu)造函數(shù)
? ? ?構(gòu)造函數(shù)是一個(gè)特殊的、與類(lèi)同名的成員函數(shù),用于給每個(gè)數(shù)據(jù)成員設(shè)置適當(dāng)?shù)某跏贾怠?
(4) 成員函數(shù)
? ? ?成員函數(shù)必須在類(lèi)內(nèi)部聲明,可以在類(lèi)內(nèi)部定義,也可以在類(lèi)外部定義。如果在類(lèi)內(nèi)部定義,就默認(rèn)是內(nèi)聯(lián)函數(shù)。
?
3 類(lèi)定義補(bǔ)充
3.1 可使用類(lèi)型別名來(lái)簡(jiǎn)化類(lèi)
? ? ?除了定義數(shù)據(jù)和函數(shù)成員之外,類(lèi)還可以定義自己的局部類(lèi)型名字。
? ? ?使用類(lèi)型別名有很多好處,它讓復(fù)雜的類(lèi)型名字變得簡(jiǎn)單明了、易于理解和使用,還有助于程序員清楚地知道使用該類(lèi)型的真實(shí)目的。
class People
{?
public:?
? ? ?typedef std::string phonenum; //電話(huà)號(hào)碼類(lèi)型
?
? ? ?phonenum phonePub; //公開(kāi)號(hào)碼
private: ? ? ?
? ? ?phonenum phonePri;//私人號(hào)碼
};?
?
3.2 成員函數(shù)可被重載
? ? ?可以有多個(gè)重載成員函數(shù),個(gè)數(shù)不限。
3.3 內(nèi)聯(lián)函數(shù)
? ? ?有三種:
(1)直接在類(lèi)內(nèi)部定義。
(2)在類(lèi)內(nèi)部聲明,加上inline關(guān)鍵字,在類(lèi)外部定義。
(3)在類(lèi)內(nèi)部聲明,在類(lèi)外部定義,同時(shí)加上inline關(guān)鍵字。注意:此種情況下,內(nèi)聯(lián)函數(shù)的定義通常應(yīng)該放在類(lèi)定義的同一頭文件中,而不是在源文件中。這是為了保證內(nèi)聯(lián)函數(shù)的定義在調(diào)用該函數(shù)的每個(gè)源文件中是可見(jiàn)的。
3.4 訪(fǎng)問(wèn)限制
? ? ?public,private,protected 為屬性/方法限制的關(guān)鍵字。
3.5 類(lèi)的數(shù)據(jù)成員中不能使用 auto、extern和register等進(jìn)行修飾, 也不能在定義時(shí)進(jìn)行初始化
? ? ?如 int xPos = 0; //錯(cuò);
例外:
? ? ? ? ? 靜態(tài)常量整型(包括char,bool)數(shù)據(jù)成員可以直接在類(lèi)的定義體中進(jìn)行初始化,例如:
? ? ? ? ? static const int ia= 30;?
?
4 類(lèi)聲明與類(lèi)定義
4.1 類(lèi)聲明(declare)
class Screen;
? ? ? 在聲明之后,定義之前,只知道Screen是一個(gè)類(lèi)名,但不知道包含哪些成員。只能以有限方式使用它,不能定義該類(lèi)型的對(duì)象,只能用于定義指向該類(lèi)型的指針或引用,聲明(不是定義)使用該類(lèi)型作為形參類(lèi)型或返回類(lèi)型的函數(shù)。
void Test1(Screen& a){};
void Test1(Screen* a){};
4.2 類(lèi)定義(define)
? ? ?在創(chuàng)建類(lèi)的對(duì)象之前,必須完整的定義該類(lèi),而不只是聲明類(lèi)。所以,類(lèi)不能具有自身類(lèi)型的數(shù)據(jù)成員,但可以包含指向本類(lèi)的指針或引用。
class LinkScreen
{
public:
? ? ? ? ? Screen window;
? ? ? ? ? LinkScreen* next;
? ? ? ? ? LinkScreen* prev;
}; //注意,分號(hào)不能丟
? ? ?因?yàn)樵陬?lèi)定義之后可以接一個(gè)對(duì)象定義列表,可類(lèi)比內(nèi)置類(lèi)型,定義必須以分號(hào)結(jié)束:
class LinkScreen{ /* ... */ };
class LinkScreen{ /* ... */ } scr1,scr2;?
? ? ?
5 類(lèi)對(duì)象
? ? ?定義類(lèi)對(duì)象時(shí),將為其分配存儲(chǔ)空間。
? ? ?Sales_item item; //編譯器分配了足以容納一個(gè) Sales_item 對(duì)象的存儲(chǔ)空間。item 指的就是那個(gè)存儲(chǔ)空間。
?
6 隱含的 this 指針?
? ? ?成員函數(shù)具有一個(gè)附加的隱含形參,即 this指針,它由編譯器隱含地定義。成員函數(shù)的函數(shù)體可以顯式使用 this 指針。
6.1 何時(shí)使用 this 指針
? ? ?當(dāng)我們需要將一個(gè)對(duì)象作為整體引用而不是引用對(duì)象的一個(gè)成員時(shí)。最常見(jiàn)的情況是在這樣的函數(shù)中使用 this:該函數(shù)返回對(duì)調(diào)用該函數(shù)的對(duì)象的引用。
class Screen?
{
...
public:
? ? ? Screen& set(char);
};
Screen& Screen::set(char c)?
{
? ? ? contents[cursor] = c;
? ? ? return *this;
}
7 類(lèi)作用域
? ? ?每個(gè)類(lèi)都定義了自己的作用域和唯一的類(lèi)型。
? ? ?類(lèi)的作用域包括:類(lèi)的內(nèi)部(花括號(hào)之內(nèi)), 定義在類(lèi)外部的成員函數(shù)的參數(shù)表(小括號(hào)之內(nèi))和函數(shù)體(花括號(hào)之內(nèi))。
class Screen?
{?
//類(lèi)的內(nèi)部
...
};?
//類(lèi)的外部
char Screen::get(index r, index c) const
{
? ? ?index row = r * width; ? ? ?// compute the row location
? ? ?return contents[row + c]; ? // offset by c to fetch specified character
}?
? ? ?注意:成員函數(shù)的返回類(lèi)型不一定在類(lèi)作用域中。可通過(guò) 類(lèi)名::來(lái)判斷是否是類(lèi)的作用域,::之前不屬于類(lèi)的作用域,::之后屬于類(lèi)的作用域。例如
Screen:: 之前的返回類(lèi)型就不在類(lèi)的作用域,Screen:: 之后的函數(shù)名開(kāi)始到函數(shù)體都是類(lèi)的作用域。
class Screen?
{?
public:?
? ? ?typedef std::string::size_type index;?
? ? ?index get_cursor() const;?
};?
Screen::index Screen::get_cursor() const ? //注意:index前面的Screen不能少
{?
? ? ?return cursor;?
}?
? ? ?該函數(shù)的返回類(lèi)型是 index,這是在 Screen 類(lèi)內(nèi)部定義的一個(gè)類(lèi)型名。在類(lèi)作用域之外使用,必須用完全限定的類(lèi)型名 Screen::index 來(lái)指定所需要的 index 是在類(lèi) Screen 中定義的名字。
?
?
二 構(gòu)造函數(shù)
? ? ?構(gòu)造函數(shù)是特殊的成員函數(shù),用來(lái)保證每個(gè)對(duì)象的數(shù)據(jù)成員具有合適的初始值。
? ? ?構(gòu)造函數(shù)名字與類(lèi)名相同,不能指定返回類(lèi)型(也不能定義返回類(lèi)型為void),可以有0-n個(gè)形參。
? ? ?在創(chuàng)建類(lèi)的對(duì)象時(shí),編譯器就運(yùn)行一個(gè)構(gòu)造函數(shù)。
?
1 構(gòu)造函數(shù)可以重載
? ? ?可以為一個(gè)類(lèi)聲明的構(gòu)造函數(shù)的數(shù)量沒(méi)有限制,只要每個(gè)構(gòu)造函數(shù)的形參表是唯一的。
class Sales_item;
{
public:?
? ? ?Sales_item(const std::string&);?
? ? ?Sales_item(std::istream&);?
? ? ?Sales_item(); //默認(rèn)構(gòu)造函數(shù)
};?
?
2 構(gòu)造函數(shù)自動(dòng)執(zhí)行?
? ? ?只要?jiǎng)?chuàng)建該類(lèi)型的一個(gè)對(duì)象,編譯器就運(yùn)行一個(gè)構(gòu)造函數(shù):
Sales_item item1("0-201-54848-8");
Sales_item *p = new Sales_item();?
? ? ?第一種情況下,運(yùn)行接受一個(gè) string 實(shí)參的構(gòu)造函數(shù),來(lái)初始化變量item1。
? ? ?第二種情況下,動(dòng)態(tài)分配一個(gè)新的 Sales_item 對(duì)象,通過(guò)運(yùn)行默認(rèn)構(gòu)造函數(shù)初始化該對(duì)象。
?
3 構(gòu)造函數(shù)初始化式
? ? ?與其他函數(shù)一樣,構(gòu)造函數(shù)具有名字、形參表和函數(shù)體。
? ? ?與其他函數(shù)不同的是,構(gòu)造函數(shù)可以包含一個(gè)構(gòu)造函數(shù)初始化列表: ?
Sales_item::Sales_item(const string &book): isbn(book), units_sold(0), revenue(0.0)
{ }?
? ? ?構(gòu)造函數(shù)初始化列表以一個(gè)冒號(hào)開(kāi)始,接著是一個(gè)以逗號(hào)分隔的數(shù)據(jù)成員列表,每個(gè)數(shù)據(jù)成員后面跟一個(gè)放在圓括號(hào)中的初始化式。
? ? ?構(gòu)造函數(shù)可以定義在類(lèi)的內(nèi)部或外部。構(gòu)造函數(shù)初始化只在構(gòu)造函數(shù)的定義中指定。
? ? ?構(gòu)造函數(shù)分兩個(gè)階段執(zhí)行:(1)初始化階段;(2)普通的計(jì)算階段。初始化列表屬于初始化階段(1),構(gòu)造函數(shù)函數(shù)體中的所有語(yǔ)句屬于計(jì)算階段(2)。
? ? ?初始化列表比構(gòu)造函數(shù)體先執(zhí)行。不管成員是否在構(gòu)造函數(shù)初始化列表中顯式初始化,類(lèi)類(lèi)型的數(shù)據(jù)成員總是在初始化階段初始化。
3.1 哪種類(lèi)需要初始化式
? ? ?const 對(duì)象或引用類(lèi)型的對(duì)象,可以初始化,但不能對(duì)它們賦值,而且在開(kāi)始執(zhí)行構(gòu)造函數(shù)的函數(shù)體之前要完成初始化。
? ? ?初始化 const 或引用類(lèi)型數(shù)據(jù)成員的唯一機(jī)會(huì)是構(gòu)造函數(shù)初始化列表中,在構(gòu)造函數(shù)函數(shù)體中對(duì)它們賦值不起作用。
? ? ?沒(méi)有默認(rèn)構(gòu)造函數(shù)的類(lèi)類(lèi)型的成員,以及 const 或引用類(lèi)型的成員,必須在初始化列表中完成初始化。
class ConstRef?
{?
public:?
? ? ?ConstRef(int ii);?
private:?
? ? ?int i;?
? ? ?const int ci;?
? ? ?int &ri;?
};?
ConstRef::ConstRef(int ii)?
{
? ? ?i = ii; ? // ok?
? ? ?ci = ii; ?// error
? ? ?ri = i; ? //?
}
? ? ?應(yīng)該這么初始化:
ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) { }?
3.2 成員初始化的次序
? ? ?每個(gè)成員在構(gòu)造函數(shù)初始化列表中只能指定一次。重復(fù)初始化,編譯器一般會(huì)有提示。
? ? ?成員被初始化的次序就是定義成員的次序,跟初始化列表中的順序無(wú)關(guān)。
3.3 初始化式表達(dá)式
? ? ?初始化式可以是任意表達(dá)式
Sales_item(const std::string &book, int cnt, double price): isbn(book), units_sold(cnt), revenue(cnt * price) { }
3.4 類(lèi)類(lèi)型的數(shù)據(jù)成員的初始化式
? ? ?初始化類(lèi)類(lèi)型的成員時(shí),要指定實(shí)參并傳遞給成員類(lèi)型的一個(gè)構(gòu)造函數(shù),可以使用該類(lèi)型的任意構(gòu)造函數(shù)。
Sales_item(): isbn(10, '9'), units_sold(0), revenue(0.0) {}
?3.5 類(lèi)對(duì)象的數(shù)據(jù)成員的初始化 ? ? ?
? ? ?在類(lèi)A的構(gòu)造函數(shù)初始化列表中沒(méi)有顯式提及的每個(gè)成員,使用與初始化變量相同的規(guī)則來(lái)進(jìn)行初始化。
? ? ?類(lèi)類(lèi)型的數(shù)據(jù)成員,運(yùn)行該類(lèi)型的默認(rèn)構(gòu)造函數(shù)來(lái)初始化。
? ? ?內(nèi)置或復(fù)合類(lèi)型的成員的初始值依賴(lài)于該類(lèi)對(duì)象的作用域:在局部作用域中不被初始化,在全局作用域中被初始化為0。假設(shè)有一個(gè)類(lèi)A,
class A
{
? ? public:
? ? ? ? int ia;
? ? ? ? B b;
};
? ? A類(lèi)對(duì)象A a;不管a在局部作用域還是全局作用域,b使用B類(lèi)的默認(rèn)構(gòu)造函數(shù)來(lái)初始化,ia的初始化取決于a的作用域,a在局部作用域,ia不被初始化,a在全局作用域,ia初始化0。
4 默認(rèn)構(gòu)造函數(shù)?
? ? ?不含形參的構(gòu)造函數(shù)就是默認(rèn)構(gòu)造函數(shù)。 ? ??
? ? ?只要定義一個(gè)對(duì)象時(shí)沒(méi)有提供初始化式,就使用默認(rèn)構(gòu)造函數(shù)。如: A a;
? ? ?為所有形參提供默認(rèn)實(shí)參的構(gòu)造函數(shù)也定義了默認(rèn)構(gòu)造函數(shù)。例如:
class A
{
public:?
? ? ?A(int a=1,char c =''){}
private: ?
? ? ?int ia;
? ? ?char c1;
};
4.1 合成的默認(rèn)構(gòu)造函數(shù)
? ? ?只有當(dāng)一個(gè)類(lèi)沒(méi)有定義構(gòu)造函數(shù)時(shí),編譯器才會(huì)自動(dòng)生成一個(gè)默認(rèn)構(gòu)造函數(shù)。
? ? ?一個(gè)類(lèi)只要定義了一個(gè)構(gòu)造函數(shù),編譯器也不會(huì)再生成默認(rèn)構(gòu)造函數(shù)。
建議:
? ? ?如果定義了其他構(gòu)造函數(shù),也提供一個(gè)默認(rèn)構(gòu)造函數(shù)。
? ? ?如果類(lèi)包含內(nèi)置或復(fù)合類(lèi)型(如 int& 或 string*)的成員,它應(yīng)該定義自己的構(gòu)造函數(shù)來(lái)初始化這些成員。每個(gè)構(gòu)造函數(shù)應(yīng)該為每個(gè)內(nèi)置或復(fù)合類(lèi)型的成員提供初始化。
?
5 隱式類(lèi)類(lèi)型轉(zhuǎn)換
5.1 只含單個(gè)形參的構(gòu)造函數(shù)能夠?qū)崿F(xiàn)從形參類(lèi)型到該類(lèi)類(lèi)型的一個(gè)隱式轉(zhuǎn)換
class A
{
public:
? ? ?A(int a)
? ? ?{
? ? ? ? ? ia =a;
? ? ?}
?
? ? ?bool EqualTo(const A& a)
? ? ?{
? ? ? ? ? return ia == a.ia;
? ? ?}
?
private:
? ? ?int ia;
};
?
A a(1);
bool bEq = false;
bEq = a.EqualTo(1);//參數(shù)為1,實(shí)現(xiàn)從int型到A的隱式轉(zhuǎn)換
?
5.2抑制由構(gòu)造函數(shù)定義的隱式轉(zhuǎn)換
? ? ?通過(guò)將構(gòu)造函數(shù)聲明為 explicit,來(lái)防止在需要隱式轉(zhuǎn)換的上下文中使用構(gòu)造函數(shù):?
class A
{
public:
? ? ?explicit A(int a )
? ? ?{
? ? ? ? ? ia =a;
? ? ?}
?
? ? ?bool EqualTo(const A& a)
? ? ?{
? ? ? ? ? return ia == a.ia;
? ? ?}
?
private:
? ? ?int ia;
};
? ? ?通常,除非有明顯的理由想要定義隱式轉(zhuǎn)換,否則,單形參構(gòu)造函數(shù)應(yīng)該為 explicit。將構(gòu)造函數(shù)設(shè)置為 explicit 可以避免錯(cuò)誤。
?
?
三 復(fù)制控制
1 復(fù)制構(gòu)造函數(shù)
1.1 幾個(gè)要點(diǎn)
(1) 復(fù)制構(gòu)造函數(shù)
? ? ?復(fù)制構(gòu)造函數(shù)是一種特殊構(gòu)造函數(shù),只有1個(gè)形參,該形參(常用 const &修飾)是對(duì)該類(lèi)類(lèi)型的引用。
class Peopel
{
public:
? ? ?Peopel();//默認(rèn)構(gòu)造函數(shù)
? ? ?Peopel(const Peopel&);//復(fù)制構(gòu)造函數(shù)
? ? ?~Peopel();//析構(gòu)函數(shù)
};
? ? ?當(dāng)定義一個(gè)新對(duì)象并用一個(gè)同類(lèi)型的對(duì)象對(duì)它進(jìn)行初始化時(shí),將顯式使用復(fù)制構(gòu)造函數(shù)。
Peopel a1; Peopel a2 = a1;
? ? ?當(dāng)將該類(lèi)型的對(duì)象傳遞給函數(shù)或函數(shù)返回該類(lèi)型的對(duì)象時(shí),將隱式使用復(fù)制構(gòu)造函數(shù)。
Peopel Func(Peopel b){...}
(2)析構(gòu)函數(shù)
? ? ?析構(gòu)函數(shù)是構(gòu)造函數(shù)的互補(bǔ):當(dāng)對(duì)象超出作用域或動(dòng)態(tài)分配的對(duì)象被刪除時(shí),將自動(dòng)應(yīng)用析構(gòu)函數(shù)。
? ? ?析構(gòu)函數(shù)可用于釋放構(gòu)造對(duì)象時(shí)或在對(duì)象的生命期中所獲取的資源。
? ? ?不管類(lèi)是否定義了自己的析構(gòu)函數(shù),編譯器都自動(dòng)執(zhí)行類(lèi)中非 static 數(shù)據(jù)成員的析構(gòu)函數(shù)。
(3) 復(fù)制控制
? ? ?復(fù)制構(gòu)造函數(shù)、賦值操作符和析構(gòu)函數(shù)總稱(chēng)為復(fù)制控制。編譯器自動(dòng)實(shí)現(xiàn)這些操作,但類(lèi)也可以定義自己的版本。
(4) 兩種初始化形式
? ? ?C++ 支持兩種初始化形式:直接初始化和復(fù)制初始化。直接初始化將初始化式放在圓括號(hào)中,復(fù)制初始化使用 = 符號(hào)。
? ? ?對(duì)于內(nèi)置類(lèi)型,例如int, double等,直接初始化和復(fù)制初始化沒(méi)有區(qū)別。
? ? ?對(duì)于類(lèi)類(lèi)型:直接初始化直接調(diào)用與實(shí)參匹配的構(gòu)造函數(shù);復(fù)制初始化先使用指定構(gòu)造函數(shù)創(chuàng)建一個(gè)臨時(shí)對(duì)象,然后用復(fù)制構(gòu)造函數(shù)將那個(gè)臨時(shí)對(duì)象復(fù)制到正在創(chuàng)建的對(duì)象。直接初始化比復(fù)制初始化更快。
(5)形參和返回值
? ? ?當(dāng)形參或返回值為類(lèi)類(lèi)型時(shí),由該類(lèi)的復(fù)制構(gòu)造函數(shù)進(jìn)行復(fù)制。?
(6)初始化容器元素
? ? ?復(fù)制構(gòu)造函數(shù)可用于初始化順序容器中的元素。例如:
vector<string> svec(5);
? ? ?編譯器首先使用 string 默認(rèn)構(gòu)造函數(shù)創(chuàng)建一個(gè)臨時(shí)值,然后使用復(fù)制構(gòu)造函數(shù)將臨時(shí)值復(fù)制到 svec 的每個(gè)元素。?
(7)構(gòu)造函數(shù)與數(shù)組元素
? ? ?如果沒(méi)有為類(lèi)類(lèi)型數(shù)組提供元素初始化式,則將用默認(rèn)構(gòu)造函數(shù)初始化每個(gè)元素。
? ? ?如果使用常規(guī)的花括號(hào)括住的數(shù)組初始化列表來(lái)提供顯式元素初始化式,則使用復(fù)制初始化來(lái)初始化每個(gè)元素。根據(jù)指定值創(chuàng)建適當(dāng)類(lèi)型的元素,然后用復(fù)制構(gòu)造函數(shù)將該值復(fù)制到相應(yīng)元素:
Sales_item primer_eds[] = { string("0-201-16487-6"),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?string("0-201-54848-8"),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?string("0-201-82470-1"),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Sales_item()
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?};
1.2 合成的復(fù)制構(gòu)造函數(shù)
(1)合成的復(fù)制構(gòu)造函數(shù)
? ? ?如果沒(méi)有定義復(fù)制構(gòu)造函數(shù),編譯器就會(huì)為我們合成一個(gè)。
? ? ?合成復(fù)制構(gòu)造函數(shù)的行為是,執(zhí)行逐個(gè)成員初始化,將新對(duì)象初始化為原對(duì)象的副本。
逐個(gè)成員初始化:合成復(fù)制構(gòu)造函數(shù)直接復(fù)制內(nèi)置類(lèi)型成員的值,類(lèi)類(lèi)型成員使用該類(lèi)的復(fù)制構(gòu)造函數(shù)進(jìn)行復(fù)制。
例外:如果一個(gè)類(lèi)具有數(shù)組成員,則合成復(fù)制構(gòu)造函數(shù)將復(fù)制數(shù)組。復(fù)制數(shù)組時(shí)合成復(fù)制構(gòu)造函數(shù)將復(fù)制數(shù)組的每一個(gè)元素。
1.3 定義自己的復(fù)制構(gòu)造函數(shù)
(1) 只包含類(lèi)類(lèi)型成員或內(nèi)置類(lèi)型(但不是指針類(lèi)型)成員的類(lèi),無(wú)須顯式地定義復(fù)制構(gòu)造函數(shù),也可以復(fù)制。?
class Peopel
{
public:
? ? ?std::string name;
? ? ?unsigned int id;
? ? ?unsigned int age;
? ? ?std::string address;
};
(2) 有些類(lèi)必須對(duì)復(fù)制對(duì)象時(shí)發(fā)生的事情加以控制。
? ? ?例如,類(lèi)有一個(gè)數(shù)據(jù)成員是指針,或者有成員表示在構(gòu)造函數(shù)中分配的其他資源。而另一些類(lèi)在創(chuàng)建新對(duì)象時(shí)必須做一些特定工作。這兩種情況下,都必須定義自己的復(fù)制構(gòu)造函數(shù)。
? ? ?最好顯式或隱式定義默認(rèn)構(gòu)造函數(shù)和復(fù)制構(gòu)造函數(shù)。如果定義了復(fù)制構(gòu)造函數(shù),必須定義默認(rèn)構(gòu)造函數(shù)。
?
1.4 禁止復(fù)制
? ? ?有些類(lèi)需要完全禁止復(fù)制。例如,iostream 類(lèi)就不允許復(fù)制。延伸:容器內(nèi)元素不能為iostream?
? ? ?為了防止復(fù)制,類(lèi)必須顯式聲明其復(fù)制構(gòu)造函數(shù)為 private。
2 賦值操作符
? ? ?與復(fù)制構(gòu)造函數(shù)一樣,如果類(lèi)沒(méi)有定義自己的賦值操作符,則編譯器會(huì)合成一個(gè)。
(1)重載賦值操作符
Sales_item& operator=(const Sales_item &);
(2)合成賦值操作符
? ? ?合成賦值操作符會(huì)逐個(gè)成員賦值:右操作數(shù)對(duì)象的每個(gè)成員賦值給左操作數(shù)對(duì)象的對(duì)應(yīng)成員。除數(shù)組之外,每個(gè)成員用所屬類(lèi)型的常規(guī)方式進(jìn)行賦值。對(duì)于數(shù)組,給每個(gè)數(shù)組元素賦值。
(3)復(fù)制和賦值常一起使用?
? ? ?一般而言,如果類(lèi)需要復(fù)制構(gòu)造函數(shù),它也會(huì)需要賦值操作符。?
3 析構(gòu)函數(shù)
? ? ?構(gòu)造函數(shù)的用途之一是自動(dòng)獲取資源;與之相對(duì)的是,析構(gòu)函數(shù)的用途之一是回收資源。除此之外,析構(gòu)函數(shù)可以執(zhí)行任意類(lèi)設(shè)計(jì)者希望在該類(lèi)對(duì)象的使用完畢之后執(zhí)行的操作。
(1) 何時(shí)調(diào)用析構(gòu)函數(shù)
撤銷(xiāo)(銷(xiāo)毀)類(lèi)對(duì)象時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)。
變量(類(lèi)對(duì)象)在超出作用域時(shí)應(yīng)該自動(dòng)撤銷(xiāo)(銷(xiāo)毀)。
動(dòng)態(tài)分配的對(duì)象(new A)只有在指向該對(duì)象的指針被刪除時(shí)才撤銷(xiāo)(銷(xiāo)毀)。
撤銷(xiāo)(銷(xiāo)毀)一個(gè)容器(不管是標(biāo)準(zhǔn)庫(kù)容器還是內(nèi)置數(shù)組)時(shí),也會(huì)運(yùn)行容器中的類(lèi)類(lèi)型元素的析構(gòu)函數(shù)(容器中的元素總是從后往前撤銷(xiāo))。
(2)何時(shí)編寫(xiě)顯式析構(gòu)函數(shù)
? ? ?如果類(lèi)需要定義析構(gòu)函數(shù),則它也需要定義賦值操作符和復(fù)制構(gòu)造函數(shù),這個(gè)規(guī)則常稱(chēng)為三法則:如果類(lèi)需要析構(gòu)函數(shù),則需要所有這三個(gè)復(fù)制控制成員。
(3)合成析構(gòu)函數(shù)
? ? ?合成析構(gòu)函數(shù)按對(duì)象創(chuàng)建時(shí)的逆序撤銷(xiāo)每個(gè)非 static 成員,因此,它按成員在類(lèi)中聲明次序的逆序撤銷(xiāo)成員。
? ? ?對(duì)于每個(gè)類(lèi)類(lèi)型的成員,合成析構(gòu)函數(shù)調(diào)用該成員的析構(gòu)函數(shù)來(lái)撤銷(xiāo)對(duì)象。
? ? ?合成析構(gòu)函數(shù)并不刪除指針成員所指向的對(duì)象。 所以,如果有指針成員,一定要定義自己的析構(gòu)函數(shù)來(lái)刪除指針。
? ? ?析構(gòu)函數(shù)與復(fù)制構(gòu)造函數(shù)或賦值操作符之間的一個(gè)重要區(qū)別:即使我們編寫(xiě)了自己的析構(gòu)函數(shù),合成析構(gòu)函數(shù)仍然運(yùn)行。
?
?
四 友元
? ? ?友元機(jī)制允許一個(gè)類(lèi)將對(duì)其非公有成員的訪(fǎng)問(wèn)權(quán)授予指定的函數(shù)或類(lèi)。
? ? ?友元可以出現(xiàn)在類(lèi)定義的內(nèi)部的任何地方。
? ? ?友元不是授予友元關(guān)系的那個(gè)類(lèi)的成員,所以它們不受聲明出現(xiàn)部分的訪(fǎng)問(wèn)控制影響。
? ? ?建議:將友元聲明成組地放在類(lèi)定義的開(kāi)始或結(jié)尾。
?
1 友元類(lèi)
class Husband
{
public:
? ? ?friend class Wife;
private:
? ? ?double money;//錢(qián)是老公私有的,別人不能動(dòng),但老婆除外
};
?
class Wife
{
public:
? ? ?void Consume(Husband& h)
? ? ?{
? ? ? ? ? h.money -= 10000;//老婆可以花老公的錢(qián)
? ? ?}
};
?
Husband h;
Wife w;
w.Consume(h);
?
2 使其他類(lèi)的成員函數(shù)成為友元
class Husband; //1.聲明Husband?
?
class Wife //2.定義Wife類(lèi)?
{
public:
? ? ?void Consume(Husband& h);
};
?
class Husband //3.定義Husband類(lèi)
{
public:
? ? ?friend void Wife::Consume(Husband& h);//聲明Consume函數(shù)。
private:
? ? ?double money;//錢(qián)是老公私有的,別人不能動(dòng),但老婆除外
};
?
void Wife::Consume(Husband& h) //4.定義Consume函數(shù)。
{
? ? ?h.money -= 10000;//老婆可以花老公的錢(qián)
}
注意類(lèi)和函數(shù)的聲明和定義的順序:
(1)聲明類(lèi)Husband?
(2)定義類(lèi)Wife,聲明Consume函數(shù)
(3)定義類(lèi)Husband
(4)定義Consume函數(shù)。
?
?
五 static 類(lèi)成員
?
static 成員,有全局對(duì)象的作用,但又不破壞封裝。
1 static 成員變量
static 數(shù)據(jù)成員是與類(lèi)關(guān)聯(lián)的對(duì)象,并不與該類(lèi)的對(duì)象相關(guān)聯(lián)。
static 成員遵循正常的公有/私有訪(fǎng)問(wèn)規(guī)則。 ?
2 使用 static 成員而不是全局對(duì)象有三個(gè)優(yōu)點(diǎn)。
(1) ?static 成員的名字是在類(lèi)的作用域中,因此可以避免與其他類(lèi)的成員或全局對(duì)象名字沖突。
(2) ?可以實(shí)施封裝。static 成員可以是私有成員,而全局對(duì)象不可以。
(3) ?通過(guò)閱讀程序容易看出 static 成員是與特定類(lèi)關(guān)聯(lián)的,這種可見(jiàn)性可清晰地顯示程序員的意圖。?
3 static 成員函數(shù)
? ? ?在類(lèi)的內(nèi)部聲明函數(shù)時(shí)需要添加static關(guān)鍵字,但是在類(lèi)外部定義函數(shù)時(shí)就不需要了。
? ? ?因?yàn)閟tatic 成員是類(lèi)的組成部分但不是任何對(duì)象的組成部分,所以有以下幾個(gè)特點(diǎn):
1) static 函數(shù)沒(méi)有 this 指針
2) static 成員函數(shù)不能被聲明為 const (將成員函數(shù)聲明為 const 就是承諾不會(huì)修改該函數(shù)所屬的對(duì)象)
3) static 成員函數(shù)也不能被聲明為虛函數(shù)
4 static 數(shù)據(jù)成員?
? ? ?static 數(shù)據(jù)成員可以聲明為任意類(lèi)型,可以是常量、引用、數(shù)組、類(lèi)類(lèi)型,等等。
? ? ?static 數(shù)據(jù)成員必須在類(lèi)定義體的外部定義(正好一次),并且應(yīng)該在定義時(shí)進(jìn)行初始化。
建議:定義在類(lèi)的源文件中名,即與類(lèi)的非內(nèi)聯(lián)函數(shù)的定義同一個(gè)文件中。注意,定義時(shí)也要帶上類(lèi)類(lèi)型+"::"
double Account::interestRate = 0.035;?
5 特殊的靜態(tài)常量整型成員?
? ? ?靜態(tài)常量整型數(shù)據(jù)成員可以直接在類(lèi)的定義體中進(jìn)行初始化,例如:
static const int period = 30;?
? ? ?當(dāng)然char 可以轉(zhuǎn)換成整形,也是可以的, ? static const char bkground = '#';
?
6 其他
(1)static 數(shù)據(jù)成員的類(lèi)型可以是該成員所屬的類(lèi)類(lèi)型。非 static 成員只能是自身類(lèi)對(duì)象的指針或引用?
class Screen?
{
public:
? ? ? ? ?// ...
private:
? ? ? ? ?static Screen src1; // ok
? ? ? ? ?Screen *src2; ? ? ? // ok
? ? ? ? ?Screen src3; ? ? ? ?// error
};?
(2)非 static 數(shù)據(jù)成員不能用作默認(rèn)實(shí)參,static 數(shù)據(jù)成員可用作默認(rèn)實(shí)參
class Screen?
{
public:
? ? ? ? ? Screen& clear(char = bkground);
private:
? ? ? ? ?static const char bkground = '#';//static const整形變量可以在類(lèi)內(nèi)部初始化。
};
========
C++創(chuàng)建對(duì)象的三種方式
http://blog.csdn.net/azhexg/article/details/14225545C++中有三種創(chuàng)建對(duì)象的方法
#include <iostream> ?
using namespace std; ?
class A ?
{ ?
private: ?
? ? int n; ?
public: ?
? ? A(int m):n(m) ?
? ? { } ?
? ? ~A(){} ?
}; ?
int main() ?
{ ?
? ? A a(1); ?//棧中分配 ?
? ? A b = A(1); ?//棧中分配 ?
? ? A* c = new A(1); ?//堆中分配 ?
delete c; ?
? ? return 0; ?
} ?
第一種和第二種沒(méi)什么區(qū)別,一個(gè)隱式調(diào)用,一個(gè)顯式調(diào)用,兩者都是在進(jìn)程虛擬地址空間中的棧中分配內(nèi)存,而第三種使用了new,在堆中分配了內(nèi)存,而棧中內(nèi)存的分配和釋放是由系統(tǒng)管理,而堆中內(nèi)存的分配和釋放必須由程序員手動(dòng)釋放。采用第三種方式時(shí),必須注意一下幾點(diǎn)問(wèn)題:
new創(chuàng)建類(lèi)對(duì)象需要指針接收,一處初始化,多處使用
new創(chuàng)建類(lèi)對(duì)象使用完需delete銷(xiāo)毀
new創(chuàng)建對(duì)象直接使用堆空間,而局部不用new定義類(lèi)對(duì)象則使用棧空間
new對(duì)象指針用途廣泛,比如作為函數(shù)返回值、函數(shù)參數(shù)等
頻繁調(diào)用場(chǎng)合并不適合new,就像new申請(qǐng)和釋放內(nèi)存一樣
棧的大小遠(yuǎn)小于堆的大
棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會(huì)在底層對(duì)棧提供支持:分配專(zhuān)門(mén)的寄存器存放棧的地址,壓棧出棧都有專(zhuān)門(mén)的指令執(zhí)行,這就決定了棧的效率 比較高。堆則是C/C++函數(shù)庫(kù)提供的,它的機(jī)制是很復(fù)雜的,例如為了分配一塊內(nèi)存,庫(kù)函數(shù)會(huì)按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在 堆內(nèi)存中搜索可用的足夠大小的空間,如果沒(méi)有足夠大小的空間(可能是由于內(nèi)存碎片太多),就有可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機(jī)會(huì) 分 到足夠大小的內(nèi)存,然后進(jìn)行返回。顯然,堆的效率比棧要低得多。
========
C++用new來(lái)創(chuàng)建對(duì)象和非new來(lái)創(chuàng)建對(duì)象的區(qū)別
http://www.cnblogs.com/GODYCA/archive/2013/01/10/2854777.html我們都知道C++中有三種創(chuàng)建對(duì)象的方法,如下:
#include <iostream>
using namespace std;
class A
{
private:
? ? int n;
public:
? ? A(int m):n(m)
? ? {
? ? }
? ? ~A(){}
};
int main()
{
? ? A a(1); ?//棧中分配
? ? A b = A(1); ?//棧中分配
? ? A* c = new A(1); ?//堆中分配
delete c;
? ? return 0;
}
第一種和第二種沒(méi)什么區(qū)別,一個(gè)隱式調(diào)用,一個(gè)顯式調(diào)用,兩者都是在進(jìn)程虛擬地址空間中的棧中分配內(nèi)存,而第三種使用了new,在堆中分配了內(nèi)存,而棧中內(nèi)存的分配和釋放是由系統(tǒng)管理,而堆中內(nèi)存的分配和釋放必須由程序員手動(dòng)釋放,所以這就產(chǎn)生一個(gè)問(wèn)題是把對(duì)象放在棧中還是放在堆中的問(wèn)題,這個(gè)問(wèn)題又和堆和棧本身的區(qū)別有關(guān):
這里面有幾個(gè)問(wèn)題:
1.堆和棧最大可分配的內(nèi)存的大小
2.堆和棧的內(nèi)存管理方式
3.堆和棧的分配效率
首先針對(duì)第一個(gè)問(wèn)題,一般來(lái)說(shuō)對(duì)于一個(gè)進(jìn)程棧的大小遠(yuǎn)遠(yuǎn)小于堆的大小,在linux中,你可以使用ulimit -s (單位kb)來(lái)查看一個(gè)進(jìn)程棧的最大可分配大小,一般來(lái)說(shuō)不超過(guò)8M,有的甚至不超過(guò)2M,不過(guò)這個(gè)可以設(shè)置,而對(duì)于堆你會(huì)發(fā)現(xiàn),針對(duì)一個(gè)進(jìn)程堆的最大可分配的大小在G的數(shù)量級(jí)上,不同系統(tǒng)可能不一樣,比如32位系統(tǒng)最大不超過(guò)2G,而64為系統(tǒng)最大不超過(guò)4G,所以當(dāng)你需要一個(gè)分配的大小的內(nèi)存時(shí),請(qǐng)用new,即用堆。
其次針對(duì)第二個(gè)問(wèn)題,棧是系統(tǒng)數(shù)據(jù)結(jié)構(gòu),對(duì)于進(jìn)程/線(xiàn)程是唯一的,它的分配與釋放由操作系統(tǒng)來(lái)維護(hù),不需要開(kāi)發(fā)者來(lái)管理。在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí),這些存儲(chǔ)單元會(huì)被自動(dòng)釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,不同的操作系統(tǒng)對(duì)棧都有一定的限制。 堆上的內(nèi)存分配,亦稱(chēng)動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的期間用malloc申請(qǐng)的內(nèi)存,這部分內(nèi)存由程序員自己負(fù)責(zé)管理,其生存期由開(kāi)發(fā)者決定:在何時(shí)分配,分配多少,并在何時(shí)用free來(lái)釋放該內(nèi)存。這是唯一可以由開(kāi)發(fā)者參與管理的內(nèi)存。使用的好壞直接決定系統(tǒng)的性能和穩(wěn)定。
由上可知,但我們需要的內(nèi)存很少,你又能確定你到底需要多少內(nèi)存時(shí),請(qǐng)用棧。而當(dāng)你需要在運(yùn)行時(shí)才知道你到底需要多少內(nèi)存時(shí),請(qǐng)用堆。
最后針對(duì)第三個(gè)問(wèn)題,棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會(huì)在底層對(duì)棧提供支持:分配專(zhuān)門(mén)的寄存器存放棧的地址,壓棧出棧都有專(zhuān)門(mén)的指令執(zhí)行,這就決定了棧的效率 比較高。堆則是C/C++函數(shù)庫(kù)提供的,它的機(jī)制是很復(fù)雜的,例如為了分配一塊內(nèi)存,庫(kù)函數(shù)會(huì)按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在 堆內(nèi)存中搜索可用的足夠大小的空間,如果沒(méi)有足夠大小的空間(可能是由于內(nèi)存碎片太多),就有可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機(jī)會(huì) 分 到足夠大小的內(nèi)存,然后進(jìn)行返回。顯然,堆的效率比棧要低得多。
由上可知,能用棧則用棧。
復(fù)制代碼
#include <stdio.h>
#include <stdlib.h> ?
void main()
{
?int n,*p,i,j,m;
?printf("本程序可對(duì)任意個(gè)整數(shù)排序;\n");
?printf("請(qǐng)輸入整數(shù)的總個(gè)數(shù): ");
?scanf("%d",&n);
?p=(int *)calloc(n,sizeof(int)); ? ?//運(yùn)行時(shí)決定內(nèi)存分配大小
?if(p==0) ? {
? printf("分配失敗!\n"); ?
? exit(1); ?
?}
========
總結(jié)
以上是生活随笔為你收集整理的C++类和对象学习总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: SQL CTE学习总结
- 下一篇: Python 安全编程学习总结