文章目錄
1.類的三種訪問限定符
public、protected、private
2.實例化類的對象與訪問對象成員
定義一個TV類:
class TV{
public:
char name[20];
int type;
void changeVol();
void power();
}
從棧中實例化:
int main(void){
TV tv;
TV tv[20];
return 0;
}
從堆中實例化:
int main(void){
TV *p = new TV();
TV *q = new TV[20];
// todo(如果從堆中,一定要手動釋放)
delete p;
delete []q;
p = nullptr;
q = nullptr;
return 0;
}
注意:要區分是棧還是堆也很簡單,堆我們需要手動分配、釋放內存(new、delete)。
訪問對象成員(棧):
int main(void){
TV tv;
tv.type = 0;
return 0;
}
訪問對象成員(堆):
int main(void){
TV *p = new TV();
p->type = 0;
p->changeVol();
delete p;
p = nullptr;
return 0;
}
訪問對象成員(數組):
int main(void){
TV *p = new TV[5];
for(int i = 0; i < 5; i++){
p[i]->type = 0;
p[i]->changeVol();
}
delete []p;
p = nullptr;
return 0;
}
3.String字符串的使用
常用方法:
s.empty()
s.size()
s1+s2
字符串連接:
string s1 = "hello";
string s2("world");
string s3 = s1 + s2;
string s4 = "hello" + s2;
string s5 = "hello" + s2 + "world";
// 錯誤
string s6 = "hello" + "world";
注意: 字符串連接,只能用於“字符串+變量”,不能“字符串+字符串”
4.new和delete運算符-內存的創建與釋放
new/delete 是C++的運算符;類似於malloc/free,程序運行(動態)得開闢內存空間(堆);
new 可以爲內置類型的變量開闢空間,數組變量,類的對象開闢空間。
這是在堆上開闢內存,返回一個指向該內存空間的地址。
new/delete會調用類的構造函數和析構函數(malloc不會)。
5.在類的外部定義類函數
注意:類內定義的函數優先選擇編譯爲內聯函數(inline)。
class Car{
public:
void run();
void stop();
void changeSpeed();
};
void Car::run(){}
void Car::stop(){}
void Car::changeSpeed(){}
如果需要 分文件 進行定義:
car.h
// 存放類的聲明
class Car{
public:
void run();
void stop();
void changeSpeed();
};
car.cpp
// 存放類的定義,切記要包含(include)頭文件
void Car::run(){}
void Car::stop(){}
void Car::changeSpeed(){}
6.內存區塊與代碼之間的聯繫
區 | |
---|---|
棧區 | int x = 0; int *p = nullptr; |
堆區 | int *p = new int[20]; |
全局區 | 存儲全局變量及靜態變量 |
常量區 | string str = “hello”; |
代碼區 | 存儲邏輯代碼的二進制 |
7.構造函數
構造函數一定是沒有返回值的。在參數上,如果沒有參數,我們都稱之其爲默認構造函數。
如果是默認構造函數(不帶參數的構造函數),在實例化的時候,有如下幾種寫法:
// 在堆中創建
Coordinate *p1 = new Coordinate();
Coordinate *p2 = new Coordinate;
// 在棧中創建
Coordinate p2;
如果在堆中進行創建,那我們也需要手動進行釋放,代碼如下:
delete p1;
p1 = nullptr;// 不是必要,只是防止delete失敗,所以這裏再將指針置爲安全狀態
// 如果是類數組
delete[] p;
p1 = nullptr;
8.構造函數-初始化列表
初始化列表是連接到構造函數名後面的,初始化列表和構造函數一樣能夠初始化類中的屬性,但是對於const修飾的常量,只能用 初始化列表 進行初始化。即如果需要初始化,必須寫到 初始化列表 當中。
- 初始化列表先於構造函數執行
- 初始化列表,只能用於構造函數
- 初始化列表可以同時初始化多個數據成員
例子1,對const修飾的常量賦初值:
// 錯誤,採用構造函數
class Circle{
public:
Circle():{m_dPi=3.14}
private:
const double m_dPi;
};
// 正確,採用初始化列表
class Circle{
public:
Circle():m_dPi(3.14){}
private:
const double m_dPi;
};
例2,多參數賦值:
// ====== 類聲明START ======
class Teacher{
public:
// 構造函數(沒有返回值),這裏設定了默認參數
Teacher(string name = "Jim",int age = 1,int max = 50);
// 操作類的屬性
void setName(string name);
string getName() const;
void setAge(int age);
int getAge();
private:
// 屬性
string m_strName; // 名字
int m_iAge; // 年齡
};
// ====== 類聲明END ======
// ====== 類實現START ======
// 構造函數(在初始化列表中,初始化屬性)
Teacher::Teacher(string name,int age,int max):m_strName(name),m_iAge(age){
cout << "This is the constructor!" << endl;
}
// 操作類的屬性
void Teacher::setName(string name) {m_strName = name;}
string Teacher::getName() const {return m_strName;}
void Teacher::setAge(int age) {m_iAge = age;}
int Teacher::getAge() {return m_iAge;}
// ====== 類實現END ======
int main(int argc,char *argv[]) {
Teacher tea;
cout << "Name:" << tea.getName() << endl;
cout << "Age:" <<tea.getAge() << endl;
return 0;
}
初始化列表的成員初始化順序:
C++初始化類成員時,是按照聲明的順序初始化的,而不是按照出現在初始化列表中的順序。
// 構造函數的聲明
class CMyClass {
CMyClass(int x, int y);
int m_x;
int m_y;
};
// 構造函數的實現
CMyClass::CMyClass(int x, int y) : m_y(y), m_x(m_y){
}
你可能以爲上面的代碼將會首先做m_y=y
,然後做m_x=m_y
,最後m_x
、m_y
有相同的值。
但是編譯器先初始化m_x
,然後是m_y
,因爲它們是按這樣的順序 聲明 的。
結果是m_x將有一個不可預測的值。
有兩種方法避免它,一個是總是按照你希望它們被初始化的順序聲明成員;第二個是,如果你決定使用初始化列表,總是按照它們 聲明的順序 羅列這些成員。這將有助於消除混淆。
9.拷貝構造函數
格式:類名(const類名&變量名)
,變量名可寫可不寫。
拷貝構造函數和構造函數一樣,也可以在函數名後面添加初始化列表,如果沒有去實現它,系統也會自動生成一個默認的。
拷貝構造函數的參數是確定的,不能重載。
在下面的3中情況下,系統自動調用 拷貝構造函數 ,此時不會調用 構造函數 !
- 一個對象作爲函數參數,以值傳遞的方式傳入函數體;
- 一個對象作爲函數返回值,以值傳遞的方式從函數返回;
- 一個對象用於給另外一個對象進行初始化(常稱爲賦值初始化);
例1,使用 賦值初始化 來創建新的對象 :
class stu{
public:
// 構造函數
stu(){
cout << "stu" << endl;
}
// 拷貝構造函數
stu(const stu& stu01){
cout << "stu01" << endl;
}
};
int main(int argc,char *argv[]){
stu stu01; // 調用構造函數
stu stu02 = stu01; // 調用拷貝構造函數
stu stu03(stu01); // 調用拷貝構造函數
return 0;
}
/* 輸出:
stu
stu01
stu01
*/
這裏創建了3個實例,但是實際上只有第一次調用了構造函數,之後兩次調用的都是拷貝構造函數。
例2,當類作爲 函數參數 傳遞時,將觸發拷貝構造函數:
class stu{
public:
// 構造函數
stu(){
cout << "stu" << endl;
}
// 拷貝構造函數
stu(const stu&){
cout << "stu01" << endl;
}
};
// 當作爲函數參數傳遞時
void test(stu t){
}
int main(int argc,char *argv[]){
stu stu01; // 調用構造函數
test(stu01); // 調用拷貝構造函數
return 0;
}
/* 輸出:
stu
stu01
*/
例3,當對象作爲 返回值 時,觸發拷貝構造函數:
// 請看筆記中的封裝篇(下)中的第3章:對象指針
// 其中有相關的實例代碼
拷貝構造函數中,如何獲取引用&的值、如何手寫拷貝構造函數:
在本例中,部分類的屬性操作方法的後面添加const
,將其變爲const成員函數(常對象函數),以便我們在拷貝構造函數中使用 &
引用 來調用他們獲取數據。
// ====== 類聲明START ======
class Teacher{
public:
// 構造函數(沒有返回值),這裏設定了默認參數
Teacher(string name = "Jim",int max = 50);
// 拷貝構造函數
Teacher(const Teacher& tea);
// 操作類的屬性
void setName(string name);
string getName() const;
int getMax() const;
private:
// 屬性
string m_strName; // 名字
const int m_iMax; // 最多帶的學生數
};
// ====== 類聲明END ======
// ====== 類實現START ======
// 構造函數(在初始化列表中,初始化屬性)
Teacher::Teacher(string name,int max):m_strName(name),m_iMax(max){
cout << "02" << endl;
}
// 拷貝構造函數(在初始化列表中,初始化屬性)
Teacher::Teacher(const Teacher &teaRaw):m_strName(teaRaw.getName()),m_iMax(teaRaw.getMax()) {
cout << "03" << endl;
cout << "teaRaw.getName() = " << teaRaw.getName() << endl;
cout << "teaRaw.getMax() = " << teaRaw.getMax() << endl;
}
// 操作類的屬性
// name
void Teacher::setName(string name) {m_strName = name;}
string Teacher::getName() const {return m_strName;}
// max
int Teacher::getMax() const {return m_iMax;}
// ====== 類實現END ======
int main(int argc,char *argv[]) {
// 將調用構造函數
Teacher tea;
cout << "Name:" << tea.getName() << endl;
cout << "Max:" << tea.getMax() << endl;
// 將調用拷貝構造函數(在這一步中,將展示如何獲取引用&的值)
Teacher tea01 = tea;
cout << "Name:" << tea01.getName() << endl;
cout << "Max:" << tea01.getMax() << endl;
return 0;
}
/* 輸出:
02
Name:Jim
Max:50
03
teaRaw.getName() = Jim
teaRaw.getMax() = 50
Name:Jim
Max:50
*/
這樣我們就能夠在 拷貝構造函數 中獲取到tea
這個對象的值了。
這裏需要再次強調,如果在類的屬性由const修飾,意味着我們只能在 初始化 的時候爲其賦值,其它的時刻任何賦值操作都會報錯,所以我們不能在構造函數/拷貝構造函數的{ }
爲其賦值,只能在 初始化列表 中賦值,且之後再無法修改。
注意: 關於const 成員函數的聲明,const 關鍵字只能放在函數聲明的尾部,看起來確實是怪怪的。
10.析構函數
格式:~類名()
,不能加任何參數。
成員函數除了析構函數之外,都可以有重載。
本篇爲視頻教程筆記,視頻如下: