1. 類和對象
1.1 聲明類
類包含:屬性(數(shù)據(jù)),方法(類成員的函數(shù))
使用關(guān)鍵字class聲明,如聲明一個(gè)人類
//使用關(guān)鍵字class聲明一個(gè)人類
class Human
{
//數(shù)據(jù)屬性 data attributes
string Name;
string DateOfBirth;
string PlacwOfBirth;
//方法 method
void Talk(string TextToTalk);
void IntroduceSelf();
}
1.2 實(shí)例化對象
實(shí)例化對象的語法:
//實(shí)例化對象
Human Tom; //在棧上實(shí)例化一個(gè)對象
Human *pAHuman = new Human(); //在堆上實(shí)例化一個(gè)對象
delete pAHuman; //刪除對象
1.3 使用句點(diǎn)運(yùn)算符訪問類成員
//使用句點(diǎn)運(yùn)算符訪問類成員
// 訪問類成員數(shù)據(jù)和類成員函數(shù)
Human Tom;
Tom.DateOfBirth="1990";
Tom.IntroduceSelf;
//指針pTom形式
Human* pTom = new Human();
(*pTom).IntroduceSelf();
1.4 使用指針運(yùn)算符(->)訪問成員
//使用指針運(yùn)算符(->)訪問成員
//Human Tom;
//Human *Ptom = &Tom;
Human* pTom = new Human();
pTom->DataOfBirth = "1970";
pTom->IntroduceSelf();
2. 關(guān)鍵字public和private
public和private兩個(gè)關(guān)鍵字能夠控制類書香的訪問和操縱方式。一般而言,需要用private修飾類成員數(shù)據(jù),以保護(hù)數(shù)據(jù)不被暴力修改。面向?qū)ο缶幊讨行薷膶ο蟮臄?shù)據(jù)需要優(yōu)雅地使用類成員方法對數(shù)據(jù)進(jìn)行修改。
//一個(gè)包含private和public修飾的類
class Human
{
private:
//私有成員數(shù)據(jù)
int Age;
string name;
public:
int GetAge()
{
return Age;
}
int SetAge(int InputAge)
{
Age = InputAge;
}
//...其他成員...
};
2.1 private關(guān)鍵字實(shí)現(xiàn)數(shù)據(jù)抽象
private關(guān)鍵字的特點(diǎn):private指定了不能從類外訪問的信息(成員數(shù)據(jù)和成員函數(shù)),對外界隱藏類使用者無需知道的東西
#include<iostream>
using namespace std;
class Human
{
private:
//private member data
int Age;
public:
void SetAge(int InputAge)
{
Age = InputAge;
}
int GetAge()
{
if(Age > 30)
return (Age - 2);
else
return Age;
}
};
int main()
{
Human FirstMan;
FirstMan.SetAge(45);
Human FirstWoman;
FirstWoman.SetAge(23);
cout << "Age of FirstMan "<< FirstMan.GetAge() <<endl;
cout << "Age of FirstWoman " << FirstWoman.GetAge() <<endl;
return 0;
}
3. 構(gòu)造函數(shù)
構(gòu)造函數(shù)是一種特殊的方法,在創(chuàng)建對象時(shí)使用。和普通的函數(shù)一樣,可以被重載。
3.1 聲明和實(shí)現(xiàn)構(gòu)造函數(shù)
構(gòu)造函數(shù)的名稱和類名相同
//聲明構(gòu)造函數(shù)
class Human
{
public:
Human()
{
// 構(gòu)造函數(shù)的代碼
}
};
//在類外定義構(gòu)造函數(shù)
class Human
{
public:
Human();
};
//類外提供構(gòu)造函數(shù)的實(shí)現(xiàn)
Human::Human()
{
//構(gòu)造函數(shù)的代碼
}
3.2 何時(shí)使用構(gòu)造函數(shù)
構(gòu)造函數(shù)在創(chuàng)建對象時(shí)被調(diào)用,構(gòu)造函數(shù)可將成員變量(int, string, 指針)初始化為已知值。
3.3 重載構(gòu)造函數(shù)
構(gòu)造函數(shù)可以被重載
可以選擇不實(shí)現(xiàn)默認(rèn)構(gòu)造函數(shù),從而要求實(shí)例化對象時(shí)必須提供某些參數(shù),以使用重載的構(gòu)造函數(shù)。
//重載構(gòu)造函數(shù)
class Human
{
public:
Human()
{
//默認(rèn)構(gòu)造函數(shù)
}
Human(string HumansName)
{
//重載構(gòu)造函數(shù)
}
};
3.4 沒有默認(rèn)構(gòu)造函數(shù)的類
如果沒有默認(rèn)構(gòu)造函數(shù),而提供重載的構(gòu)造函數(shù)時(shí),編譯器不會(huì)生產(chǎn)默認(rèn)構(gòu)造函數(shù)。
這樣可以使創(chuàng)建每個(gè)對象時(shí),都必須提供某些對象的參數(shù)。
3.5 帶默認(rèn)值的構(gòu)造函數(shù)參數(shù)
//帶默認(rèn)值的構(gòu)造函數(shù)參數(shù)
class Human
{
private:
string Name;
int Age;
public:
// 重載構(gòu)造函數(shù)
Human(string HumansName, int HumansAge = 25)
{
Name = HumansName;
Age = HumansAge;
cout << "overload constructor creates " << Name;
cout << " of age " << Age <<endl;
}
// other members
};
3.6 包含初始化列表的構(gòu)造函數(shù)
//包含初始化列表的構(gòu)造函數(shù)
class Human
{
private:
string Name;
int Age;
public:
Human(string InputName, int InputAge):Name(InputName),Age(InputAge)
{
cout << "Constructor with parameters: ";
cout << "Human " << Name <<", "<< Age << " years old"<<endl;
}
};
4. 析構(gòu)函數(shù)
析構(gòu)函數(shù)和構(gòu)造函數(shù)一樣,也是一種特殊的函數(shù)。析構(gòu)函數(shù)在對象銷毀時(shí)自動(dòng)被調(diào)用。
析構(gòu)函數(shù)不能被重載。
如果忘記實(shí)現(xiàn)析構(gòu)函數(shù),則編譯器會(huì)自動(dòng)生成一個(gè)偽(dummy)析構(gòu)函數(shù)并調(diào)用它,偽析構(gòu)函數(shù)為空,不會(huì)釋放動(dòng)態(tài)分配的內(nèi)存。
4.1 聲明和實(shí)現(xiàn)析構(gòu)函數(shù)
析構(gòu)函數(shù)的聲明
//析構(gòu)函數(shù)的聲明
class Human
{
public:
~Human()
{
//銷毀成員數(shù)據(jù)的代碼
}
};
4.2 何時(shí)使用析構(gòu)函數(shù)
當(dāng)對象不在作用域或通過delete刪除時(shí),將調(diào)用析構(gòu)函數(shù)對其進(jìn)行銷毀。析構(gòu)函數(shù)成為了重置變量和釋放內(nèi)存的一種有效手段。
//用類封裝一個(gè)c風(fēng)格的字符串
#include<iostream>
using namespace std;
class MyString
{
private:
char* Buffer;
public:
// constructor
MyString(const char* InitialInput)
{
if(InitialInput != NULL)
{
Buffer = new char [strlen(InitialInput)+1];
strcpy(Buffer,InitialInput);
}
else
Buffer = NULL;
}
// distructor
~MyString()
{
cout << "Invoking destructor, cleaning up! "<<endl;
if(Buffer != NULL)
{
delete[] Buffer;
}
}
int GetLength()
{
return strlen(Buffer);
}
const char* GetString()
{
return Buffer;
}
};
int main()
{
MyString SayHello("hello from string class");
cout << "Length: " << SayHello.GetLength() << endl;
cout << "content: " << SayHello.GetString() << endl;
return 0;
}
5. 復(fù)制構(gòu)造函數(shù)
調(diào)用函數(shù)時(shí),如果函數(shù)的形參是對象,則傳遞的實(shí)參對象也被復(fù)制,因此需要復(fù)制構(gòu)造函數(shù)。
5.1 淺復(fù)制存在的問題
淺復(fù)制是指在傳遞對象時(shí),參數(shù)被復(fù)制過程中僅復(fù)制了指向內(nèi)存的指針變量,而沒有復(fù)制包含內(nèi)容的內(nèi)存塊。

被淺復(fù)制的對象在某個(gè)函數(shù)中脫離作用域時(shí)就被析構(gòu),然而主調(diào)函數(shù)中往往還要對其進(jìn)行一次析構(gòu),這就出現(xiàn)了問題。
5.2 使用復(fù)制構(gòu)造函數(shù)確保深復(fù)制
聲明構(gòu)造函數(shù)的語法:
//拷貝構(gòu)造函數(shù)的實(shí)現(xiàn)
class MyString
{
Mystring const(const MyString& CopySource);
};
MyString::MyStrin(const MyString& CopySource)
{
//拷貝構(gòu)造函數(shù)實(shí)現(xiàn)代碼
};
注意!
- 類包含原始指針成員時(shí),必須要變細(xì)人復(fù)制構(gòu)造函數(shù)和復(fù)制賦值運(yùn)算符;
- 編寫復(fù)制構(gòu)造函數(shù)時(shí),必須要將接受原對象的參數(shù)聲明為const引用;
- 必須將類成員聲明為std:string和智能指針類(而非原始指針),因?yàn)樗麄儗?shí)現(xiàn)了復(fù)制構(gòu)造函數(shù),可以減少工作量;
- 除非萬不得已,不要將類成員聲明為原始指針;
5.3 C++11中的移動(dòng)構(gòu)造函數(shù)
如果遇到構(gòu)造函數(shù)被連續(xù)調(diào)用兩次的情況,實(shí)際上是不需要將一個(gè)對象反復(fù)構(gòu)造兩次的,此時(shí)應(yīng)該使用移動(dòng)復(fù)制構(gòu)造函數(shù)
使用移動(dòng)復(fù)制構(gòu)造函數(shù)時(shí),C++11編譯器會(huì)自動(dòng)使用它“移動(dòng)”臨時(shí)資源,避免深復(fù)制。
//拷貝構(gòu)造函數(shù)被連續(xù)調(diào)用兩次的情況
class MyString
{
//...
};
MyString Copy(MyString& Source) //復(fù)制string對象的函數(shù)
{
MyString CopyForReturn(Source.GetString);
return CopyForReturn;
}
int main()
{
MyString sayHello("hello!");
MyString sayHelloAgain(Copy(sayHello)); //執(zhí)行兩次拷貝構(gòu)造函數(shù),一次copy返回,一次是作為參數(shù)傳入
return 0;
}
//移動(dòng)復(fù)制構(gòu)造函數(shù)的聲明
MyString(MyString&& MoveSource)
{
if(MoveSource.Buffer != NULL)
{
Buffer = MoveSource.Buffer;
MoveSource.Buffer = NULL;
}
}
6. 構(gòu)造函數(shù)和析構(gòu)函數(shù)的其他用途
6.1 不允許復(fù)制的類(單例模式)
用private修飾所有的構(gòu)造函數(shù)(默認(rèn)構(gòu)造函數(shù),復(fù)制構(gòu)造函數(shù)...)
用static修飾一個(gè)函數(shù),這個(gè)函數(shù)中實(shí)例化了一個(gè)對象。從而使得該類中僅存在這一個(gè)對象。
注意:
- 關(guān)鍵字static用于累的數(shù)據(jù)成員時(shí),該數(shù)據(jù)成員將在所有的實(shí)例中共享
- 將static用于函數(shù)中聲明的局部變量時(shí),該變量的值將在兩次調(diào)用之間保持不變
- 將static用于成員函數(shù)時(shí),該方法將在所有實(shí)例之間共享
6.3禁止在棧中實(shí)例化的類
如果要編寫一個(gè)數(shù)據(jù)庫類,且內(nèi)部結(jié)構(gòu)超過1GB,那么就應(yīng)該禁止在棧上實(shí)例化該類,棧的空間沒有那么大。
關(guān)鍵在于兩點(diǎn):
- 在棧上實(shí)例化對象時(shí),將類的析構(gòu)函數(shù)用private進(jìn)行修飾,從而使得每次對象脫離作用域時(shí),無法調(diào)用析構(gòu)函數(shù),編譯器會(huì)檢查該錯(cuò)誤;而當(dāng)在堆上實(shí)例化對象時(shí),這種方法編譯器不會(huì)報(bào)錯(cuò),而導(dǎo)致內(nèi)存泄露。
- 需要提供一個(gè)用來銷毀實(shí)例的靜態(tài)公有函數(shù),只有這個(gè)類成員函數(shù)才能夠調(diào)用析構(gòu)函數(shù)。
7. this指針
- 在類中,this指針包含當(dāng)前對象的地址
- 靜態(tài)成員方法不會(huì)傳遞this指針,因?yàn)殪o態(tài)方法與實(shí)例不關(guān)聯(lián),由所有實(shí)例共享
8. sizeof()用于類
sizeof()是一個(gè)運(yùn)算符,用于確定指定類型需要多少內(nèi)存。
類實(shí)例化為對象后所占的內(nèi)存實(shí)際上是在設(shè)計(jì)類時(shí)就確定了,與結(jié)構(gòu)體類似,也有內(nèi)存對齊的要求。
類對象所占的內(nèi)存由類的數(shù)據(jù)成員決定。如int, bool, char* 等。
9. 結(jié)構(gòu)體和類的區(qū)別
除非指定,類的成員默認(rèn)為私有的,結(jié)構(gòu)體的成員默認(rèn)為公有的。
10. 聲明友元
聲明友元函數(shù)和友元類時(shí),則友元類和友元函數(shù)可以從類外訪問類的私有數(shù)據(jù)成員和方法。
友元函數(shù)的聲明
//友元函數(shù)的聲明
private:
//...
friend void function();
//...
//友元類的聲明
//...
private:
friend class Human;
//...