笔记 黑马程序员C++教程从0到1入门编程——核心编程
目錄
1 內存分區模型
程序運行前
01 代碼區
02 全局區
程序運行后
03 棧區
04 堆區
05 new操作符
2 引用
06 引用的基本使用
07 引用注意事項
08 引用做函數參數
09 引用做函數返回值
10 引用的本質
11 常量引用
3 函數提高
12函數默認參數
13 函數占位參數
14 函數重載
4 類和對象
封裝
16 封裝的意義 屬性和行為作為整體
對象特性:對象的初始化和清理
23 構造函數和析構函數
24 構造函數的分類和調用
?25 拷貝構造函數調用時機
26 構造函數調用規則
27 深拷貝和淺拷貝
28 初始化列表
29 類對象作為類成員
30 靜態成員
對象特性:C++對象模型和this指針
32 成員變量和成員函數分開存儲
33 this指針
34 空指針訪問成員函數
35 const修飾成員函數
友元
36 全局函數做友元
37 友元類
38 成員函數做友元
C++運算符重載
39 加號運算符重載?
40 左移運算符重載
41 遞增運算符重載
42 賦值運算符重載
43 關系運算符重載
44 函數調用運算符重載
繼承
45 基本語法
46 繼承方式
47 繼承中的對象模型
48 構造和析構順序
49 同名成員處理
50 同名靜態成員處理
51 多繼承語法
52 菱形繼承問題以及解決方法
多態
53 多態的基本語法
54 多態的原理剖析
56 純虛函數和抽象函數
58 虛析構和純虛析構
5 文件操作
文本文件
143 寫文件
144 讀文件
二進制文件
145 寫文件
146 讀文件
1 內存分區模型
程序運行前
程序運行運行前,編譯后:
01 代碼區
代碼區:存放函數的二進制代碼,有操作系統進行管理
【共享,只讀】?
02 全局區
全局區:存放全局變量,靜態變量(static),常量(字符串常量和const修飾的全局常量(全局常量))
【在程序結束后由操作系統釋放】
//不在全局區的數據:局部變量、const修飾的局部變量(局部常量)?
程序運行后
03 棧區
棧區:由編譯器自動分配釋放,存放函數的形參,局部變量等
//不要返回局部變量的地址,棧區開辟的數據由編譯器自動釋放
04 堆區
堆區:由程序員分配和釋放,若程序員不釋放,系統結束時由操作系統回收
//new可以創建堆區數據,返回的是創建的堆區的地址,要用指針接收
05 new操作符
? int* arr = new int[10];//堆區開辟數組
delete[] arr;//釋放數組
2 引用
06 引用的基本使用
作用:給變量起別名
語法:數據類型 &別名=原名;
07 引用注意事項
引用必須初始化
引用初始化后不可以改變
08 引用做函數參數
引用傳遞讓形參修飾實參,簡化指針修改實參?
通過引用參數產生的效果同按地址傳遞是一樣的,引用的語法更清楚簡單
//引用傳遞 void mySwap(int &a,int &b) {int temp = a;a = b;b = temp; }int main() {int a = 1;int b = 2;cout << "a=" << a << endl;//1cout << "b=" << b << endl;//2mySwap(a, b);cout << "swap a=" << a << endl;//2cout << "swap b=" << b << endl;//1system("pause");return 0; }09 引用做函數返回值
作用:引用是可以作為函數的返回值存在的
注意:不要返回局部變量引用
用法:函數返回值是引用時,函數調用可以作為左值
//返回局部變量引用,非法操作 int& test01() {int a = 10;//局部變量存放在棧區return a; }//返回靜態變量引用,可以執行 int& test02() {static int a = 10;//靜態變量,存放在全局區,系統釋放return a; }int main() {//1.不要返回局部變量的引用int& ref = test01();cout << ref << endl;//第一次正確,因為編譯器做了保留//cout << ref << endl;//第二次錯誤,因為a的內存已經釋放int& ref2 = test02();cout << ref2 << endl;//10cout << ref2 << endl;//10cout << ref2 << endl;//10//2.如果函數的返回值是引用,這個函數的調用可以作為左值進行賦值操作test02() = 1000;cout << ref2 << endl;//1000cout << ref2 << endl;//1000system("pause");return 0; }10 引用的本質
引用的本質在C++內部實現是一個指針常量
(指針的指向是不可以修改,指針指向的值是可以改動)
11 常量引用
用來修飾形參,防止誤操作
(在函數形參列表中,可以加const修飾形參,防止形參改變實參)
void showValue(const int& val)//函數中利用常量引用防止誤操作修改實參 {cout << val; } int main() {//常量引用:用來修飾形參,防止誤操作int a = 10;//int& ref = 10;//err,引用必須引一塊合法的內存空間const int& ref = 10;//編譯器幫修改為int temp=10;int &ref=temp;//ref = 20;//加入const之后,變為只讀,不可修改int a = 100;showValue(a);system("pause");return 0; }3 函數提高
12函數默認參數
語法:返回值類型 函數名(形參=默認值){ }
- C++中函數的形參列表中的形參是可以有默認值的
- 如果有傳入數據,就用傳入的數據,如果沒有就用默認值
- 如果某個位置已經有了默認參數,那么這個位置之后,從左到右都必須有默認值
- 聲明和實現只能有一個有默認參數;如果函數聲明有默認參數,函數實現就不能有默認參數
13 函數占位參數
C++中函數形參列表里可以有占位參數,用來占位,調用函數時必須填補該位置
語法:返回值類型 函數名(數據類型){}
?? ?//占位參數還可以有默認參數 (int=10),有默認值時,調用時可以不用傳值
14 函數重載
函數名可以相同,提高復用性
滿足條件:
函數的返回值不可以作為函數重載的條件
注意事項:
4 類和對象
面向對象三大特性:封裝、繼承、多態
萬物是對象,有屬性和行為
具有相同性質的對象,可以抽象為類
封裝
16 封裝的意義 屬性和行為作為整體
//設計一個圓類,求周長
const double PI = 3.14;class Circle {//訪問權限:公共權限 public ://屬性int m_r;//半徑//行為:獲取圓的周長double calculateZC(){return 2 * PI * m_r;} };//實例化(通過一個類,創建一個對象的過程)Circle c1;//通過圓類,創建具體的圓(對象)c1.m_r = 10;//給圓的對象的屬性賦值cout << "圓的周長為:" << c1.calculateZC() << endl;實例化:通過一個類,創建一個對象的過程
類中的屬性和行為,統稱為成員
- 屬性 ?成員屬性 成員變量
- 行為 成員函數 成員方法
類在設計時,可以把屬性和行為放在不同的權限下,加以控制
18?三種訪問權限
//公共權限 public? ? ? ? ? 成員類內可以訪問,類外可以訪問
//保護權限 protected? ? 類內可以訪問,類外不可以訪問? ? ? ? ? ? 兒子可以訪問父親中的保護內容
//私有權限 private? ? ? ? 類內可以訪問,類外不可以訪問? ? ? ? ? ? 兒子不可以訪問父親中的私有內容
19 struct和class的區別:
默認的訪問權限不同?
struct默認權限是? 公共? public
class默認權限是? ?私有? private
20 成員屬性設置為私有的優點
//設計立方體類
//創建立方體類 class Cube { public:void setL(int l){m_L = l;}void setW(int w){m_W = w;}void setH(int h){m_H = h;}int getL(){return m_L ;}int getW(){return m_W;}int getH(){return m_H;}int calculateS(){return 2 * m_L * m_W + 2 * m_H * m_W + 2 * m_L * m_H;}int calculateV(){return m_L * m_W * m_H;}//利用局部函數判斷是否相等bool isSamebyclass(Cube& c){if (getL() == c.getL() && getW() == c.getW() && getH() == c.getH())return true;elsereturn false;} private:int m_L;int m_W;int m_H; };//利用全局函數判斷兩個立方體是否相等 bool isSame(Cube& c1, Cube& c2) {if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH())return true;elsereturn false; }int main() {Cube c1;c1.setL(10);c1.setW(10);c1.setH(10);cout<<"面積為"<<c1.calculateS()<<endl;cout << "體積為" << c1.calculateV() << endl;Cube c2;c2.setL(11);c2.setW(10);c2.setH(10);cout << "面積為" << c2.calculateS() << endl;cout << "體積為" << c2.calculateV() << endl;//利用全局函數判斷bool ret=isSame(c1,c2);if (ret)cout << "相等" << endl;elsecout << "不相等" <<endl;//利用局部函數判斷bool ret1 = c1.isSamebyclass(c2);if (ret1)cout << "相等" << endl;elsecout << "不相等" << endl;system("pause");return 0; }//判斷點和圓的關系?
在類中可以讓另一個類 作為本類中的成員
可以把一個類拆在不同文件中 .h聲明 .cpp實現
對象特性:對象的初始化和清理
每個對象也會有初識設置以及對象銷毀前的清理數據的設置
23 構造函數和析構函數
編譯器自動調用,完成對象初始化和清理工作
- 構造函數:創建對象時對象的成員屬性賦值
- 析構函數:對象銷毀前,清理工作
構造函數:類名(){}
析構函數:~類名(){}
24 構造函數的分類和調用
構造函數分類:
按照參數:有參構造和無參構造(默認構造)
按照類型:普通構造和拷貝構造
調用方式:
1.括號法2.顯示法3.隱式轉換法
?25 拷貝構造函數調用時機
拷貝構造函數調用時機三種情況:
26 構造函數調用規則
調用規則:
如果我們寫了有參構造函數,編譯器就不再提供默認構造,依然提供拷貝構造
如果我們寫了拷貝構造函數,編譯器就不再提供其他普通構造函數?
27 深拷貝和淺拷貝
淺拷貝:簡單的復制拷貝操作
淺拷貝帶來的問題就是堆區的內存重復釋放
深拷貝:在堆區重新申請空間,進行拷貝操作
//如果屬性有在堆區開辟的,一定要自己提供拷貝構造函數,防止淺拷貝帶來的問題
28 初始化列表
//初始化列表Person(int a,int b,int c) :m_A(a), m_B(b), m_C(c){} //屬性賦初值 Person p(31,20,10);29 類對象作為類成員
類中的成員可以是另一個類的對象,我們稱該成員為對象成員
- 當其他類對象作為本類成員,構造時候先構造類對象,再構造自身
- 當其他類對象作為本類成員,析構時候先析構自身,再析構類對象
構造順序與析構順序相反
class Phone { public:Phone(string pName){m_PName = pName;cout << "phone的構造函數調用" << endl;}~Phone(){cout << "phone的析構函數調用" << endl;}string m_PName; }; class Person { public:Person(string name, string pName):m_Name(name),m_phone(pName){cout << "person的構造函數調用" << endl;}~Person(){cout << "person的析構函數調用" << endl;}string m_Name;Phone m_phone; };30 靜態成員
靜態成員變量:
- 所有對象共享同一份數據
- 在編譯階段分配內存(程序運行前-全局區)
- 類內聲明,類外初始化
靜態成員變量,不屬于某個對象上,所有對象都共享同一份數據?
有兩種訪問方式:
靜態成員變量有訪問權限,類外訪問不到私有靜態成員變量
31 靜態成員函數
- 所有對象共享同一個函數
- 靜態成員函數只能訪問靜態成員變量
//兩種訪問方式:通過對象,通過類名
//靜態成員函數也是有訪問權限的,類外訪問不到私有的靜態成員函數
對象特性:C++對象模型和this指針
32 成員變量和成員函數分開存儲
只有非靜態成員變量 屬于對象上的數據
??空對象占用內存空間為:1
?? ?//C++編譯器會給每個空對象也分配一個字節空間,是為了區分空對象占內存的位置
?? ?//每個空對象也應該有一個獨一無二的內存地址
33 this指針
this指針:指向被調用的成員函數所屬的對象
this指針是隱含每一個非靜態成員函數內的一種指針
this指針不需要定義,直接使用
用途:
//當形參和成員變量同名時,可用this指針來區分
//在類的非靜態成員函數中返回對象本身,可使用return *this
34 空指針訪問成員函數
C++中允許空指針調用成員函數,也要注意有沒有用到this指針
//如果用到this指針,需要判斷保證代碼的健壯性
35 const修飾成員函數
常函數
成員函數后加const稱為常函數
//常函數內不可以修改成員屬性(指針指向的值不能修改)
//成員屬性聲明時加關鍵字mutable后,在常函數中依然可以修改
常對象
聲明對象前加const稱為常對象
//常對象只能調用常函數
友元
36 全局函數做友元
class Building {friend void goodgay(Building* builiding);//goodgay是Building好朋友,可以訪問Builing中私有成員 public:Building(){m_SittingRoom = "客廳";m_BedRoom = "臥室";} public:string m_SittingRoom;private:string m_BedRoom;}; //全局函數 void goodgay(Building *builiding) {cout << "全局函數正在訪問:" << builiding->m_BedRoom << endl;cout << "全局函數正在訪問:" << builiding->m_SittingRoom << endl; }37 友元類
friend class goodgay;//goodgay類是本類的好朋友,可以訪問本類中私有的成員38 成員函數做友元
friend void Goodgay::visit01();//goodgay類下的visit成員函數作為本類的好朋友,可以訪問私有成員C++運算符重載
運算符重載:對已有運算符重新進行定義,賦予另一種功能,以適應不同的數據類型
39 加號運算符重載?
作用:實現兩個自定義數據類型相加的運算
//對于內置的數據類型表達式運算符是不可能改變的
//不要濫用運算符重載
40 左移運算符重載
輸出左移運算符配合友元可以實現輸出自定義數據類型
class Person {friend ostream& operator<<(ostream& cout, Person& p); public:Person(int a, int b){m_A = a;m_B = b;} private://不會利用成員函數重載左移運算符<<,因為無法實現cout在左側int m_A;int m_B;};//只能利用全局函數重載左移運算符 ostream& operator<<(ostream &cout,Person &p)//本質是operator<<(cout,p) 簡化cout<<p {cout << "m_A=" << p.m_A << endl;cout << "m_B=" << p.m_B << endl;return cout;//為了可以無限往后追加輸出 } void test01() {Person p(10, 10);cout << p <<"hello"<< endl;; }41 遞增運算符重載
通過重載遞增運算符,實現自己的整形數據
前置遞增返回參數,后置遞增返回值??
//++//自定義整形 class MyInteger {friend ostream& operator<<(ostream& cout, MyInteger myint); public:MyInteger(){m_Num = 0;}//重載++運算符//1.重載前置++ MyInteger& operator++()//返回引用是為了一直對一個數據進行遞增操作{//先進行++運算m_Num++;//再將自身返回return *this;}//2.重載后置++ //返回的是值,因為返回的是局部變量MyInteger operator++(int)//int代表占位參數,可以用于區分前置和后置遞增,編譯器會認為這是后置遞增重載{//先記錄當時結果MyInteger temp = *this;//后遞增m_Num++;//再返回之前記錄的結果return temp;} private:int m_Num; };//重載左移運算符 ostream& operator<<(ostream& cout, MyInteger myint) {cout << myint.m_Num << endl;return cout; }void test01() {MyInteger myint;cout << ++(++myint) << endl; } void test02() {MyInteger myint;cout << myint++ << endl;cout << myint << endl; }test01();test02();42 賦值運算符重載
Person& operator=(Person& p){//編譯器提供的是淺拷貝//m_Age=p.m_Age;//應該先判斷有屬性在堆區,如果有先釋放干凈,然后再深拷貝if (m_Age != NULL){delete m_Age;m_Age = NULL;}m_Age=new int(*p.m_Age);//深拷貝return *this;}43 關系運算符重載
//重載==bool operator==(Person& p){if (this->m_Age == p.m_Age && this->m_Name == p.m_Name){return true;}else false;}//重載!=bool operator!=(Person& p){if (this->m_Age == p.m_Age && this->m_Name == p.m_Name){return false;}else true;}44 函數調用運算符重載
函數調用運算符()也可以重載
由于重載后使用的方式非常像函數的調用,因此稱為仿函數
仿函數沒有固定的寫法,非常靈活
//打印輸出 class MyPrint { public://重載函數調用運算符void operator()(string test){cout << test << endl;} }; void test01() {MyPrint myPrint;myPrint("hello");//由于使用起來類似于函數調用,稱為仿函數 } //仿函數非常靈活,沒有固定的寫法 //加法類 class MyAdd { public:int operator()(int num1, int num2){return num1 + num2;} }; void test02() {MyAdd myadd;int ret = myadd(100, 200);cout << ret << endl;//匿名函數對象 //MyAdd()cout << MyAdd()(100, 200) << endl; } int main() {test01();test02();return 0; }繼承
45 基本語法
繼承的好處:減少重復代碼
語法: class 子類:繼承方式 父類
? ? ? ? ? ? class A : publib B
????????????A類,子類也稱為派生類
????????????B類,父類也稱為基類
派生類中的成員,包含兩大部分:一類是從基類繼承過來的,一類是自己增加的成員
從基類繼承過來的表現其共性,而新增的成員體現其個性
46 繼承方式
?繼承的三種方式:
- 公共繼承
- 保護繼承
- 私有繼承
47 繼承中的對象模型
class Base { public:int a; protected:int b; private:int c;};class Son :public Base { public:int d;};void test01() {//父類中所有靜態成員屬性都會被子類繼承下去//父類中私有成員屬性是被編譯器給隱藏了,因此是訪問不到,但是確實被繼承下去了cout << "size of Son=" << sizeof(Son) << endl;//16 }利用開發人員命令提示工具(Developer Command Prompt for VS 2019)查看對象模型
//跳轉盤符 F:
//跳轉文件路徑 cd 具體路徑下
//查看命令 cl /d1 reportSingleClassLayout類名 文件名
? ? ? ? ? ? ? ? F:\C++-code\0421繼承>cl /d1 reportSingleClassLayoutSon "129對象基本模型.cpp"?
48 構造和析構順序
子類繼承父類后,當創建子類對象,也會調用父類的構造函數
繼承中的構造和析構的順序:
先構造父類,再構造子類;析構順序與構造順序相反
49 同名成員處理
當子類與父類出現同名的成員,如何通過子類對象,訪問到其中的同名數據:
1.訪問子類同名成員,直接訪問即可
2.訪問父類同名成員,需要加作用域
- 如果子類中出現和父類中同名的成員函數,子類的同名成員會隱藏掉父類中所有同名成員函數(包括重載版本)
- 如果想訪問到父類中被隱藏的同名成員函數,需要加作用域?
50 同名靜態成員處理
?同名靜態成員處理方式和非靜態處理方式一樣,只不過有兩種訪問方式(通過對象和通過類名)
class Base{public:static int m_A;static void func(){cout << "Base 的func的調用" << endl;}};int Base::m_A = 100;class Son :public Base{public:static int m_A;static void func(){cout << "Son 的func的調用" << endl;}};int Son::m_A = 200;void test01(){//通過對象訪問Son s;cout << "Son的m_A=" << s.m_A << endl;cout << "Base的m_A=" << s.Base::m_A << endl;//通過類名訪問cout << Son::m_A << endl;cout << Base::m_A << endl;cout << Son::Base::m_A << endl;//第一個::代表通過類名方式訪問,第二個::代表訪問父類作用域下的m_A}void test02(){//通過對象訪問Son s;s.func();s.Base::func();//通過類名訪問Son::func();Son::Base::func();}51 多繼承語法
C++允許一個類繼承多個類
語法:class 子類:繼承方式 父類1,繼承方式 父類2
//多繼承可能會引發父類中有同名成員出現,需要加作用域
//開發中不建議用多繼承
52 菱形繼承問題以及解決方法
菱形繼承、鉆石繼承:
- 兩個派生類繼承同一個類
- 又有某個類同時繼承兩個派生類?
多態
53 多態的基本語法
動態多態滿足條件:
動態多態使用:父類的指針或引用 指向子類對象?
54 多態的原理剖析
//示例:多態原理剖析
//動物類 class Animal { public:virtual void speak(){cout << "動物在說話" << endl;} };//貓類 class Cat :public Animal { public://虛函數void speak(){cout << "小貓在說話" << endl;} };//狗類 class Dog :public Animal { public://虛函數void speak(){cout << "小狗在說話" << endl;} }; void doSpeak(Animal &animal) {animal.speak(); }void test01() {Cat cat;doSpeak(cat);Dog dog;doSpeak(dog);}多態優點:代碼組織結構清晰,可讀性強,利于前期和后期的擴展及維護
在真實的開發中,提倡 開閉原則
開閉原則:對擴展進行開發,對修改進行關閉
56 純虛函數和抽象函數
當類中有了純虛函數,這個類就是抽象類?
?//抽象類無法實例化對象
?//子類必須重寫抽象類中的純虛函數,否則也屬于抽象類,無法實例化對象
58 虛析構和純虛析構
父類指針在析構的時候,不會調用子類中析構函數,導致子類中如果有堆區屬性,出現內存泄漏?
虛析構和純虛析構的共性:
- 可以解決父類指針釋放子類對象時不干凈的問題
- 都需要有具體的函數實現
虛析構和純虛析構的區別:
- 如果是純虛析構,該類屬于抽象類,無法實例化對象
虛析構:virtual ~類名(){}
純虛析構:virtual ~類名() = 0;//聲明
? ? ? ? ? ? ? ? ? 類名 :: ~類名() {}? //具體實現
總結:
5 文件操作
文本文件
143 寫文件
寫文件步驟:
144 讀文件
ifstream ifs; ifs.open("text.txt", ios::in); if (!ifs.is_open()) {cout << "文件打開失敗" << endl;return; } //讀文件的四種方式 //1 char buf[1024] = { 0 }; while (ifs >> buf) {cout << buf << endl; }//2 char buf[1024] = { 0 }; while (ifs.getline(buf, sizeof(buf))) {cout << buf << endl; }//3 string buf; while (getline(ifs, buf)) {cout << buf << endl; }*//4不推薦 char c; while ((c = ifs.get()) != EOF) {cout << c; }二進制文件
以二進制的方式對文件進行讀寫操作
打開方式要指定為ios::binary
145 寫文件
ofstream ofs; ofs.open("Person.txt", ios::out | ios::binary); Person p = { "張三",18 }; ofs.write((const char *)&p,sizeof(Person)); ofs.close();146 讀文件
ifstream ifs;ifs.open("person.txt", ios::in | ios::binary);if (!ifs.is_open()){cout << "文件打開失敗" << endl;return;}Person p;ifs.read((char*)&p, sizeof(Person));cout << p.m_name << p.m_age << endl;ifs.close();總結
以上是生活随笔為你收集整理的笔记 黑马程序员C++教程从0到1入门编程——核心编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 嘉立创又搞大事情了,与你我相关!
- 下一篇: C++ const限定符和auto类型说