將一組對象的共同特徵抽象出來, 從而造成類的概念.程序員
類包括數據成員和成員函數, 不能在類的聲明中對數據成員進行初始化數組
形式爲:bash
class 類名 {
private:
私有數據和函數
public:
公有數據和函數
protected:
受保護的數據和函數
}; // 注意分號
複製代碼
不管是數據成員仍是成員函數, 都是這個類的成員, 都具備一個訪問權限, 若是沒有關鍵字進行修飾, 則默認爲private權限函數
聲明一個類, 像這樣:學習
// 聲明類
class Point {
// 若是沒有修飾符, 默認爲私有的權限
double x;
double y;
public:
// 無參構造函數
Point();
// 有參構造函數
Point(double a, double b);
// 成員函數
void display();
};複製代碼
形式爲:測試
// :: 爲做用域運算符, 表示這個函數屬於哪一個類
返回類型 類名::成員函數名(參數列表) {
函數體 // 內部實現
}複製代碼
咱們在上面的聲明類的代碼中, 聲明瞭成員函數, 咱們能夠在類外面定義成員函數, 也就是給出函數體優化
像這樣:ui
// 定義成員函數
// 無參構造函數
Point::Point() {}
// 有參構造函數
Point::Point(double a, double b) {
x = a;
y = b;
}
// 可使用關鍵字inline將成員函數定義爲內聯函數
inline void Point::display() {
cout << x << ", " << y << endl;
}
複製代碼
若是在聲明類的同時, 在類體內給出成員函數的定義, 則默認爲內聯函數this
咱們通常都是在類體內存給出成員函數的定義spa
像這樣, 完成一個類的聲明和定義
// 聲明類
class Point {
double x;
double y;
public:
// 定義成員函數
Point() {}
Point(double a, double b) {
x = a;
y = b;
}
void display() { // 默認爲內聯函數
cout << x << ", " << y << endl;
}
}; 複製代碼
像這樣是不行的:
class Point {
// 在類體內不能給數據成員賦值
double x = 2;
double y = 3;
}
// 在類體外不能給數據成員賦值
// x = 4;
// y = 5;複製代碼
只有產生了具體對象, 這些數據值纔有意義
初始化: 在產生對象時就使對象的數據成員具備指定值, 則稱爲對象的初始化
賦值: 有了對象以後, 對象調用本身的成員函數實現賦值操做
類的成員函數能夠直接使用本身類的私有成員
類外面的函數不能直接訪問類的私有成員, 而只能經過類的對象使用公有成員函數
定義類對象指針的語法: 類名 * 對象指針名 = 對象地址;
經過對象指針能夠訪問對象的成員: 對象指針名 -> 對象成員名;
像這樣:
// 定義一個類
class Point {
// 聲明數據成員
double x;
double y;
public:
// 聲明而且定義成員函數
void setXY(double a, double b) {
x = a;
y = b;
}
// 一個類的成員函數能夠訪問本身的私有成員
void display() {
cout << x << ", " << y << endl;
}
};
int main() {
// 定義對象a
Point a;
// 定義b爲對象a的引用
Point & b = a;
// 定義p爲指向對象a的指針
Point *p = &a;
// 對象和引用, 都使用"."訪問對象的成員
a.setXY(2, 3);
b.setXY(4, 5);
// 指針使用"->"訪問對象的成員
p -> setXY(6, 7);
}複製代碼
一個類若是沒有定義任何構造函數, 編譯器會自動定義一個不帶參數的構造函數, 也就是默認構造函數
好比咱們有一個類Point
則默認構造函數就是這樣:
Point::Point() {};複製代碼
若是一個類提供了構造函數, 系統再也不提供默認構造函數
咱們有一個Point類, 像這樣:
class Point {
double x;
double y;
public:
Point(double a, double b) {
x = a;
y = b;
}
};複製代碼
則咱們就不能在main函數中這樣使用:
int main() {
// Point類有本身定義的構造函數Point(double, double), 因此就沒有默認構造函數Point()
// 這句話調用的是無參構造函數來定義一個對象, 因此編譯錯誤
// 咱們須要給類Point加上無參構造函數
Point a;
}複製代碼
咱們想要這樣使用, 則必須手動添加無參數構造函數
像這樣:
class Point {
double x;
double y;
public:
// 無參構造函數
Point(){}
// 有參構造函數
Point(double a, double b) {
x = a;
y = b;
}
;
int main() {
// 咱們給類加上自定義無參構造函數, 如今正確編譯
Point a;
}複製代碼
構造函數的名字應該與類名同名, 並在定義構造函數時不能指定返回類型, void也不能夠
// 聲明一個類
class Point {
double x;
double y;
public:
// 聲明無參構造函數
Point();
// 聲明有參構造函數
Point(double, double);
};
// 在類外定義構造函數
Point::Point(){}
// 第一種定義構造函數
//Point::Point(double a, double b):x(a),y(b){}
// 第二種定義構造函數
Point::Point(double a, double b) {
x = a;
y = b;
}
int main() {
// 產生對象
Point a(2, 3);
}
複製代碼
咱們通常都在類的聲明內部進行函數定義
像這樣:
// 定義一個類
class Point {
// 聲明數據成員
double x;
double y;
public:
// 無參構造函數
Point(){};
// 有參構造函數
Point(double a, double b) {
x = a;
y = b;
}
};
int main() {
// 無參構造函數 產生對象a
Point a;
// 有參構造函數 產生對象b
Point b(2, 3);
// 無參構造函數 產生對象數組arr1
Point arr1[2];
// 有參構造函數 產生對象數組arr2
Point arr2[2] = {Point(2, 3), Point(4, 5)};
}複製代碼
不能在程序中顯式地調用構造函數, 構造函數是自動調用的
即不能這樣: Point a.Point(2, 3);
只能這樣: Point a(2, 3);
用來在產生對象的同時, 進行對象的初始化
new用來創建生存期可控的動態對象, 返回這個對象的指針
new和構造函數一同起做用
過程: 當用new創建動態對象時, 首先分配能夠保存這個類對象的內存空間, 而後自動調用構造函數來初始化這塊內存, 再返回這個動態對象的地址
使用new創建的動態對象只能使用delete刪除, 以釋放所佔空間
像這樣:
// new與無參構造函數
Point * p1 = new Point;
// new與有參構造函數
Point * p2 = new Point(2, 3);
複製代碼
若是咱們定義了有參構造函數, 又想使用無參構造函數, 咱們能夠將有參構造函數的參數所有使用默認參數
像這樣:
class Point {
double x;
double y;
public:
// 聲明構造函數
// 有參構造函數
Point(double a = 0, double b = 0) {
x = a;
y = b;
}
// 成員函數
void display() {
cout << x << ", " << y << endl;
}
};
int main() {
// 產生對象
Point a;
a.display();
}複製代碼
做用: 經過拷貝方式使用一個類的已有對象來創建一個該類的新對象, 通常編譯器會創建一個默認的複製構造函數
像這樣:
類名(const 類名 &); // 爲了避免改變原有對象, 使用const來進行修飾
複製代碼
複製構造函數也能夠自定義, 則編譯器再也不調用默認的複製構造函數
像這樣:
// 定義一個類
class Point {
// 聲明數據成員
double x;
double y;
public:
// 無參構造函數
Point(){
cout << "默認構造函數" << endl;
};
// 有參構造函數
Point(double a, double b) {
x = a;
y = b;
cout << "構造函數 " << x << ", " << y << endl;
}
// 複製構造函數
Point(const Point & t) {
x = t.x;
y = t.y;
cout << "複製構造函數" << endl;
}
};
int main() {
// 使用默認構造函數產生一個對象
Point a;
// 使用複製構造函數產生一個對象
Point b(a);
}複製代碼
使用複製構造函數的三種狀況
像這樣:
// 經過構造函數實例化對象
Point a(2, 3);
// 經過構造函數實例化對象
Point b(a);
// 調用成員函數
b.display();
複製代碼
像這樣:
// 函數, 用來顯示一個Point對象
void printPoint(Point t) { // 當函數調用時形參t是經過複製構造函數來產生的對象
t.display();
} // 函數執行完畢後, 調用形參t的析構函數, 釋放內存
int main() {
// 產生對象a
Point a(2, 3);
// 調用函數
printPoint(a);
} // 函數執行完畢後, 調用對象a的析構函數, 釋放內存
複製代碼
// 函數, 獲得一個Point對象
Point getPoint() {
Point * t = new Point(2, 3);
return *t; // 產生一個對象
} // 函數執行完畢後, 調用對象t的析構函數, 釋放內存
int main() {
// 調用函數返回一個類對象, 這裏在接收函數返回的對象時會自動調用複製構造函數(不調用的是編譯器進行了優化)
Point a = getPoint();
} // 函數執行完畢後, 調用對象a的析構函數, 釋放內存
複製代碼
函數參數使用對象的引用不產生副本, 因此當對象做爲函數參數時, 推薦使用對象引用這種方式
在對象消失時, 使用析構函數釋放由構造函數分配的內存
爲了與構造函數區分, 在析構函數前加」~」號,
而且在定義析構函數時, 不能指定返回類型, 即便是void類型也不能夠;
也不能指定參數, 但能夠顯式的說明參數爲void
格式: ~類名(); // 或者 ~類名(void);
複製代碼
代碼像這樣:
~Point(); // 或者 ~Point(void);複製代碼
析構函數在對象的生存期結束時自動調用, 而後對象佔用的內存被回收
全局對象和靜態對象的析構函數在程序運行結束以前調用
類對象的數組每一個元素調用一次析構函數
像這樣: 能夠運行該代碼, 查看程序執行過程
// 定義一個類
class Point {
// 聲明數據成員
double x;
double y;
public:
// 無參構造函數
Point(){
cout << "默認構造函數" << endl;
};
// 有參構造函數
Point(double a, double b) {
x = a;
y = b;
cout << "構造函數 " << x << ", " << y << endl;
}
// 複製構造函數
Point(const Point & t) {
x = t.x;
y = t.y;
cout << "複製構造函數" << endl;
}
// 析構函數
~ Point() {
cout << "析構函數" << endl;
}
};
int main() {
// 使用默認構造函數產生一個對象
Point a; // 調用默認構造函數, 產生新對象
// 使用複製構造函數產生一個對象
Point b(a); // 調用複製構造函數, 產生新對象
// 對象數組
Point arr[2]; // 調用默認構造函數 2次, 產生兩個新對象
} // 程序結束後, 由於總共產生了4個對象, 因此也會調用4次析構函數複製代碼
當使用運算符delete刪除一個動態對象時, 首先爲這個對象調用析構函數, 而後再釋放這個動態對象佔用的內存
像這樣:
// 使用new和默認構造函數產生一個對象
Point * p = new Point;
// 使用delete來釋放內存
delete p;
// 使用new和默認構造函數產生一個對象數組, 數組有兩個對象
Point * p2 = new Point[2];
// 使用delete釋放數組內存
delete []p2;複製代碼
若是沒有定義析構函數, 編譯器自動爲類產生一個函數體爲空的默認析構函數
像這樣:
~ Point(){};複製代碼
成員函數可重載或使用默認參數, 爲了提升可讀性
// 定義一個類
class MyMax {
// 私有成員
int a, b, c, d;
// 函數: 求兩個數的最大值
int getMax(int v1, int v2) {
return v1 > v2 ? v1 : v2;
}
// 公有成員
public:
// 函數: 改變數據成員的值
void setValue(int v1, int v2, int v3 = 0, int v4 = 0) {
a = v1;
b = v2;
c = v3;
d = v4;
}
// 函數: 得到全部數據成員裏的最大值 (函數重載)
int getMax() {
return getMax(getMax(a, b), getMax(c, d));
}
};
int main() {
// 產生對象
MyMax a;
// 改變數據成員的值
a.setValue(1, 2, 3, 4);
// 調用成員函數
cout << a.getMax() << endl;
}
複製代碼
當一個成員函數被調用時, 系統自動向該函數傳遞一個隱含的參數, 指向調用該函數的對象指針, 名爲this, 從而使用成員函數知道該對哪一個對象進行操做.
做用: 它將對象和該對象調用的成員函數鏈接在一塊兒, 從外部看來, 每一個對象都擁有本身的成員函數, 但處理這些數據成員的代碼能夠被全部的對象共享
咱們通常狀況下都會省略this
// 定義一個類
class Point {
double x;
double y;
public:
// 有參構造函數
Point(double a = 0, double b = 0) {
x = a;
y = b;
}
// 成員函數
void display() {
cout << x << ", " << y << endl;
}
// 僞代碼
// void setXY(double x, double y Point * this) {
// this -> x = x;
// this -> y = y;
// }
// 咱們能夠寫成這樣
// void setXY(double x, double y) {
// this -> x = x;
// this -> y = y;
// }
// 可是通常咱們都寫成這樣
void setXY(double x, double y) {
x = x;
y = y;
}
};
int main() {
// 經過構造函數實例化對象
Point a(2, 3);
// 調用成員函數
a.display();
}
複製代碼
由於類自己就是一種新的數據類型, 因此一個類的對象能夠做爲另外一個類的成員
像這樣:
// 類: Point, 包含兩個數據成員
class Point {
double x;
double y;
public:
// 有參構造函數
Point(double a = 0, double b = 0) {
x = a;
y = b;
}
// 成員函數
void display() {
cout << x << ", " << y << endl;
}
};
// 類: Line, 包含兩個Point對象
class Line {
// 兩個爲Point類的對象做爲數據成員
Point startPoint;
Point endPoint;
public:
// 構造函數
Line(Point start, Point end) {
startPoint = start;
endPoint = end;
}
// 成員函數
void display() {
cout << "起點: ";
startPoint.display();
cout << "終點: ";
endPoint.display();
}
// 返回一個Point對象: 起點
Point getStartPoint() {
return startPoint;
}
// 返回一個Point對象: 終點
Point getEndPoint() {
return endPoint;
}
};
int main() {
// 經過構造函數實例化對象
Point a(2, 3);
Point b(4, 5);
// 經過已有對象實例化另外一個對象
Line lineA(a, b);
// 調用成員函數
lineA.display();
// 調用一個對象的成員函數, 返回另外一個對象
Point startPoint = lineA.getStartPoint();
startPoint.display();
}
複製代碼
Point a(2, 3); Point b = a;複製代碼
Point arr[3];複製代碼
Point p = &a;
p -> display();複製代碼
void print(Point a) {} // 對象做爲函數參數
void print(Point & a) {} // 對象引用做爲函數參數 (推薦使用這一種)
void print(Point * p) {} // 對象指針做爲函數參數
複製代碼
class Point {}
class Line {
Point startPoint;
Point endPoint;
}
複製代碼
類自己的成員函數可使用類的全部成員(私有和公有和受保護的成員)
類的對象只能訪問公有成員函數
其它函數不能使用類的私有成員, 也不能使用公有成員函數
雖然一個類能夠包含另外一個類的對象, 但這個類也只能經過被包含的類對象使用成員函數, 再訪問數據成員
class People; // 不徹底的類聲明
People * p; // 定義一個全局變量類指針
複製代碼
只有使用類產生對象時, 才進行內存分配
不徹底類不能進行實例化, 不然編譯出錯, 咱們使用得不是不少
class Empty {};
複製代碼
能夠不包括任何聲明, 也能夠沒有任何行爲, 但能夠產生空類對象
像這樣:
// 定義一個空類
class Empty {
public:
Empty(){};
};
int main() {
// 產生空類對象
Empty e;
}
複製代碼
做用: 在開發大型項目時, 須要在一些類尚未徹底定義或實現時進行先期測試, 保證代碼能正確地被編譯, 固然咱們有時也會給它一個無參構造函數, 來消除警告
聲明類時使用的一對花括號{}造成類的做用域, 也包括類體外成員函數的做用域.
在類做用域中聲明的標識符只在類中可見.
像這樣:
// 定義類
class Example {
int number;
public:
void setValue(int value) {
// number在類做用域在內部因此有效, 可使用
number = value;
}
void changValue(int);
};
void Example::changValue(<#int#> value) {
// 類的做用域也包括成員函數做用域, number有效
number = value;
}
//int a = number; // 錯誤, 由於這是在類做用域的外部, number無效
int number; // 正確, 這代碼定義了一個全局變量number複製代碼
每一個語言的類和對象其實大同小異, 可能一些名字不同, 可能一些格式不同, 可是思想是同樣的, 例如一個對象的產生, 都得申請內存, 而後再對這塊內存進行初始化, 有本身的屬性, 還有本身的行爲. 咱們在學習的時候不要糾結於語言的自己, 要學會總結和本身已經學過的其它語言的異同點, 從而總結出規律, 提煉出本質, 這纔是最主要的. 今天看到一段話送給你們, 大概是這麼說的: 不是咱們變老了就當不了程序員了, 而是由於咱們不想學習了, 因此才顯得咱們變老了, 因此也就當不了程序員了!