封裝篇(上)
類和對象
類的定義:
class Dog
{
public:
char name[20];
int age;
int type;
void speak();
void run();
}
選擇性暴露:封裝
訪問限定符
- public
- protected
- private
對象實例化
從棧中實例化
Dog dog;
Dog dog[20];
系統會自動回收
從堆中實例化
Dog *p = new Dog();
Dog *q = new Dog[20];
需要自行回收
對象成員的訪問
棧訪問
Dog dog;
dog.name = "哈哈";
dog.run();
return 0;
堆訪問
Dog *p = new Dog();
p->name = "哈哈";
p->run();
delete p;
p = NULL;
return 0;
char * 的操作和注意事項
-
strlen(char* s): int
: 直到碰到第一個字符串結束符’\0’爲止,然後返回計數器值strlen 與 sizeof 的區別,strlen 返回字符串長度,而 sizeof 返回 指針所佔空間大小(一般爲4)但是也可以是其他類型的傳參。
-
strcat(char *s, char* s2): void
:將 s2 拼接到 s1 的尾部 -
strcat(char *s, int size, char* s2): void
strcat 函數的安全版,中間的參數爲指定緩衝區大小 -
strcpy_s(char *s, int size, char *s2): void
:將後者字符串複製給前者 -
strcmp(char *s, char *s2): int
:串比較,看Asic碼,str1>str2,返回值 > 0;兩串相等,返回0 -
strncpy(char *s, int size, char *s2, int copyLen): void
:將 s2 的前 copyLen 個字符複製給 s1注意 size > copyLen;因爲字符串數組最後一位爲 '\0’
-
strncmp(char *s, char *s2, int cmpLen): int
:比較兩個字符串的 前 cmpLen 個字符 -
strstr(char *s, char *s2):int
:判斷 s2 是否是 s 的子串。如果是,則返回str2在str1中首次出現的地址;如果不是,則返回null;
字符串類型:string
初始化 string 對象的方式:
string s1; | s1爲空串 |
---|---|
string s2(“ABC”); | 用字符串字面值初始化s2 |
string s3(s2); | 將s3初始化爲s2的一個副本 |
string s4(n,‘c’); | 將s4初始化爲字符’c’的n個副本 |
string 的常用操作:
s.empty() | 若s 爲空串,返回 true,否則返回 false |
---|---|
s.size() | 返回s中字符的個數 |
s[n] | 返回s中位置爲n的字符,位置從0開始 |
s1+s2 | 將兩個串連接成新串,返回新生成的串 |
s1 = s2 | 把s1的內容替換成 s2 的副本 |
s1 == s2 | 判定相等 |
s1 != s2 | 判定不等 |
string s6 = "hello" + "world"; 非法
類內定義與內聯函數
類內定義
將成員函數的函數體都在類的內部定義,則爲類內定義
類內定義的成員函數,編譯器會自動添加 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 "Car.h" void Car::run() {} void Car::stop() {} void Car::changeSpeed() {}
對象結構
內存分區
-
棧區:int x = 0; int *p = NULL;
特點:內存由系統進行控制
-
堆區:int *p = new int[20];
特點:必須使用 delete 來回收
-
全局區:存儲全局變量及靜態變量
-
常量區:存儲字符串以及常量
-
代碼區:存儲邏輯代碼的二進制
對象初始化
構造函數
- 在對象實例化時被自動調用,且僅被調用一次
- 構造函數與類同名
- 構造函數沒有返回值
- 構造函數可以有多個重載形式
- 當用戶沒有定義構造函數時,編譯器自動生成
構造函數可以設置默認值;但是注意:有參構造函數全部有默認值的話會和無參構造函數衝突導致編譯無法通過
默認構造函數
不需要傳遞參數的構造函數
一個類可以沒有默認構造函數,有別的構造函數也可以實例化對象
構造函數初始化列表
初始化列表特性:
- 初始化列表先於構造函數執行
- 初始化列表只能用於構造函數
- 初始化列表可以同時初始化多個數據成員
- 初始化列表更快
初始化列表存在的必要性:
class Circle
{
public:
Circle():m_dPi(3.14) {}
private:
const double m_dPi;
}
如果將 m_dPi 的賦值放在構造函數中執行,將會報錯,因爲它是常量。
拷貝構造函數
class Student
{
public:
Student()
{ cout << "Student" << endl; }
}
Student stu1;
Student stu2 = stu1;
Student stu3(stu1);
// 打印 Student 一次
定義格式: 類名(const 類名& 變量名)
class Student
{
public:
Student(const Student& stu){}
}
拷貝構造函數性質:
- 如果沒有定義的拷貝構造函數則系統自動生成一個默認的拷貝構造函數
- 當採用直接初始化或複製初始化實例化對象時,系統自動調用拷貝構造函數
- 拷貝構造函數的參數是確定的,不能重載
傳參時也會執行拷貝構造函數
class Student {
public:
Student() {
cout << "Student" << endl;
}
Student(const Student& stu) {
cout << "拷貝構造函數" << endl;
}
};
void test(Student s) {
}
int main() {
Student stu1;
Student stu2 = stu1;
Student stu3(stu1);
test(stu1);
system("pause");
return 0;
}
// Student
// 拷貝構造函數 * 3
// 這是因爲 test 函數的傳參爲 Student 對象,觸發了拷貝構造函數
析構函數
定義格式: ~類名()
析構函數不能有參數
析構函數存在的意義:釋放資源
class Student
{
public:
Student() {
m_pName = new char[20];
}
~Student() {
delete []m_pName;
}
private:
char *m_pName;
}
析構函數性質:
- 如果沒有自定義的析構函數則系統自動生成
- 析構函數在對象銷燬時自動調用
- 析構函數沒有返回值,沒有參數,不能重載