C++繼承
//基類
class Animal{};//派生類
class Dog : public Animal{};
#include<iostearm>
using namespace std;//基類
class Shape{public:void setwidth(int w){width = w;}void setheight(int h){height = h;}protected:int width;int height;}//派生類
class Rectangle : public Shape{public:int getarea(){return width * height;}}int main(void){Rectangle Rect;Rect.setwidth(5);Rect.setheight(7);// 輸出對象的面積cout << "Total area: " << Rect.getArea() << endl;return 0;}
訪問控制與繼承
多繼承
多繼承即一個子類可以有多個父類,它繼承了多個父類的特性。
C++ 類可以從多個類繼承成員
class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>,…
{
<派生類類體>
};
C++重載運算符和重載函數
C++ 允許在同一作用域中的某個函數和運算符指定多個定義,分別稱為函數重載和運算符重載。
重載聲明是指一個與之前已經在該作用域內聲明過的函數或方法具有相同名稱的聲明,但是它們的參數列表和定義(實現)不相同。
當調用一個重載函數或重載運算符時,編譯器通過把所使用的參數類型與定義中的參數類型進行比較,決定選用最合適的定義。選擇最合適的重載函數或重載運算符的過程,稱為重載決策。
函數重載
下面的實例中,同名函數?print()?被用于輸出不同的數據類型
#include<iostearm>
using namespace std;class printdata{public:void print(int i) {cout << "整數為: " << i << endl;}void print(double f) {cout << "浮點數為: " << f << endl;}void print(char c[]) {cout << "字符串為: " << c << endl;}}int main(void)
{printdata pd;// 輸出整數pd.print(5);// 輸出浮點數pd.print(500.263);// 輸出字符串char c[] = "Hello C++";pd.print(c);return 0;
}
運算符重載
Box operator+(const Box&);
Box operator+(const Box&, const Box&);
#include <iostream>
using namespace std;class Box
{public:double getVolume(void){return length * breadth * height;}void setLength( double len ){length = len;}void setBreadth( double bre ){breadth = bre;}void setHeight( double hei ){height = hei;}// 重載 + 運算符,用于把兩個 Box 對象相加Box operator+(const Box& b){Box box;box.length = this->length + b.length;box.breadth = this->breadth + b.breadth;box.height = this->height + b.height;return box;}private:double length; // 長度double breadth; // 寬度double height; // 高度
};
// 程序的主函數
int main( )
{Box Box1; // 聲明 Box1,類型為 BoxBox Box2; // 聲明 Box2,類型為 BoxBox Box3; // 聲明 Box3,類型為 Boxdouble volume = 0.0; // 把體積存儲在該變量中// Box1 詳述Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0);// Box2 詳述Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0);// Box1 的體積volume = Box1.getVolume();cout << "Volume of Box1 : " << volume <<endl;// Box2 的體積volume = Box2.getVolume();cout << "Volume of Box2 : " << volume <<endl;// 把兩個對象相加,得到 Box3Box3 = Box1 + Box2;// Box3 的體積volume = Box3.getVolume();cout << "Volume of Box3 : " << volume <<endl;return 0;
}
C++多態
當類之間存在層次結構,并且類之間是通過繼承關聯時,就會用到多態。
C++ 多態允許使用基類指針或引用來調用子類的重寫方法,從而使得同一接口可以表現不同的行為。多態使得代碼更加靈活和通用,程序可以通過基類指針或引用來操作不同類型的對象,而不需要顯式區分對象類型。這樣可以使代碼更具擴展性,在增加新的形狀類時不需要修改主程序。
例如,假設有一個基類?Shape
?和派生類?Circle
、Rectangle
,它們都實現了?draw()
?方法。通過多態,我們可以統一調用?draw()
?方法,而具體繪制的是圓形還是矩形則由對象的實際類型決定。
class Math {
public:int add(int a, int b) { return a + b; } // 整數加法double add(double a, double b) { return a + b; } // 浮點數加法
};// 使用示例
Math m;
m.add(1, 2); // 調用 add(int, int)
m.add(1.5, 2.5); // 調用 add(double, double)
C++數據抽象
C++ 類為數據抽象提供了可能。它們向外界提供了大量用于操作對象數據的公共方法,也就是說,外界實際上并不清楚類的內部實現。實現了內部與外部的分離
#include<iostearm>
using namespace std;int main(){cout << "hello world" << endl;//此處并不需要cout是如何實現的,調用即可return 0;}
實例(有成員?addNum?和?getTotal?是對外的接口,用戶需要知道它們以便使用類。私有成員?total?是用戶不需要了解的,但又是類能正常工作所必需的):
#include<iostearm>
using namespace std;class Adder{public://構造函數Adder(int i = 0){total = i;}//對外的接口void addNum(int number){total += number;}//對外的接口int gettotal(){return total;}private://對外隱藏的數據int total;}int main(){Adder a;a.addNum(10);a.addNum(20);a.addNum(30);cout << "total is " << a.gettotal() << endl;return 0;}
C++接口(抽象類)
接口描述了類的行為和功能,而不需要完成類的特定實現。
C++ 接口是使用抽象類來實現的,抽象類與數據抽象互不混淆,數據抽象是一個把實現細節與相關的數據分離開的概念。
如果類中至少有一個函數被聲明為純虛函數,則這個類就是抽象類。純虛函數是通過在聲明中使用 "= 0" 來指定的
class BOX
{public://純虛函數virtual double getvolume() = 0;private:double length;double breadth;double height;}
設計抽象類(通常稱為 ABC)的目的,是為了給其他類提供一個可以繼承的適當的基類。抽象類不能被用于實例化對象,它只能作為接口使用。
C++文件和流
C++ 中另一個標準庫?fstream,它定義了三個新的數據類型:
要在 C++ 中進行文件處理,必須在 C++ 源代碼文件中包含頭文件 <iostream> 和 <fstream>。
open()?成員函數的第一參數指定要打開的文件的名稱和位置,第二個參數定義文件被打開的模式。
void open(const char *filename, ios::openmode mode);
可以把以上兩種或兩種以上的模式結合使用。例如,如果想要以寫入模式打開文件,并希望截斷文件,以防文件已存在,那么可以使用下面的語法:
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
如果想要打開一個文件用于讀寫
ifstream afile;
afile.open("file.dat", ios::out | ios::in );
寫入文件
在 C++ 編程中,我們使用流插入運算符( << )向文件寫入信息,就像使用該運算符輸出信息到屏幕上一樣。唯一不同的是,在這里您使用的是?ofstream?或?fstream?對象,而不是?cout?對象。
讀取文件
在 C++ 編程中,我們使用流提取運算符( >> )從文件讀取信息,就像使用該運算符從鍵盤輸入信息一樣。唯一不同的是,在這里您使用的是?ifstream?或?fstream?對象,而不是?cin?對象。
C++異常處理
異常是程序在執行期間產生的問題。C++ 異常是指在程序運行時發生的特殊情況,比如嘗試除以零的操作。
異常提供了一種轉移程序控制權的方式。C++ 異常處理涉及到三個關鍵字:try、catch、throw。
- throw:?當問題出現時,程序會拋出一個異常。這是通過使用?throw?關鍵字來完成的。
- catch:?在您想要處理問題的地方,通過異常處理程序捕獲異常。catch?關鍵字用于捕獲異常。
- try:?try?塊中的代碼標識將被激活的特定異常。它后面通常跟著一個或多個 catch 塊。
try
{// 保護代碼
}catch( ExceptionName e1 )
{// catch 塊
}catch( ExceptionName e2 )
{// catch 塊
}catch( ExceptionName eN )
{// catch 塊
}
double division(int a, int b)
{if( b == 0 ){throw "Division by zero condition!";}return (a/b);
}
C++動態內存
- 棧:在函數內部聲明的所有變量都將占用棧內存。
- 堆:這是程序中未使用的內存,在程序運行時可用于動態分配內存
可以使用特殊的運算符為給定類型的變量在運行時分配堆內的內存,這會返回所分配的空間地址。這種運算符即?new?運算符。
不再需要動態分配的內存空間,可以使用?delete?運算符,刪除之前由 new 運算符分配的內存。
new和delete運算符
我們可以定義一個指向 double 類型的指針,然后請求內存,該內存在執行時被分配
double* pvalue = NULL; // 初始化為 null 的指針
pvalue = new double; // 為變量請求內存
如果自由存儲區已被用完,可能無法成功分配內存。所以建議檢查 new 運算符是否返回 NULL 指針,并采取以下適當的操作:
double* pvalue = NULL;
if( !(pvalue = new double ))
{cout << "Error: out of memory." <<endl;exit(1);}
在任何時候,當您覺得某個已經動態分配內存的變量不再需要使用時,您可以使用 delete 操作符釋放它所占用的內存,如下所示:
delete pvalue; // 釋放 pvalue 所指向的內存
數組的動態內存分配
假設我們要為一個字符數組(一個有 20 個字符的字符串)分配內存,我們可以使用上面實例中的語法來為數組動態地分配內存,如下所示:
char* pvalue = NULL; // 初始化為 null 的指針
pvalue = new char[20]; // 為變量請求內存
要刪除我們剛才創建的數組,語句如下
delete [] pvalue; // 刪除 pvalue 所指向的數組
C++預處理器
預處理器是一些指令,指示編譯器在實際編譯之前所需完成的預處理。
#define 預處理
用于創建符號常量。該符號常量通常稱為宏
#include <iostream>
using namespace std;#define PI 3.14159int main ()
{cout << "Value of PI :" << PI << endl; return 0;
}
參數宏
可以使用 #define 來定義一個帶有參數的宏
#include <iostream>
using namespace std;#define MIN(a,b) (a<b ? a : b)int main ()
{int i, j;i = 100;j = 30;cout <<"較小的值為:" << MIN(i, j) << endl;return 0;
}
條件編譯
有幾個指令可以用來有選擇地對部分程序源代碼進行編譯。這個過程被稱為條件編譯。
#ifdef NULL#define NULL 0
#endif
#ifdef DEBUGcerr <<"Variable x = " << x << endl;
#endif
# 和 ## 運算符
# 運算符會把 replacement-text 令牌轉換為用引號引起來的字符串。
#include <iostream>
using namespace std;#define MKSTR( x ) #xint main ()
{cout << MKSTR(HELLO C++) << endl;return 0;
}
會出現這行結果:cout << MKSTR(HELLO C++) << endl;
變成了cout << "HELLO C++" << endl;
## 運算符用于連接兩個令牌
#include <iostream>
using namespace std;#define concat(a, b) a ## b
int main()
{int xy = 100;cout << concat(x, y);return 0;
}
會出現這行結果:100;將cout << concat(x, y);
變成了100;
C++多線程
線程是程序中的輕量級執行單元,允許程序同時執行多個任務。
多線程是多任務處理的一種特殊形式,多任務處理允許讓電腦同時運行兩個或兩個以上的程序。
一般情況下,兩種類型的多任務處理:基于進程和基于線程。
- 基于進程的多任務處理是程序的并發執行。
- 基于線程的多任務處理是同一程序的片段的并發執行。
并發 (Concurrency) 與并行 (Parallelism)
- 并發:多個任務在時間片段內交替執行,表現出同時進行的效果。
并行:多個任務在多個處理器或處理器核上同時執行
使用函數指針創建線程
#include <iostream>
#include <thread>void printMessage(int count) {for (int i = 0; i < count; ++i) {std::cout << "Hello from thread (function pointer)!\n";}
}int main() {std::thread t1(printMessage, 5); // 創建線程,傳遞函數指針和參數t1.join(); // 等待線程完成return 0;
}
線程管理
join()
join() 用于等待線程完成執行。如果不調用 join() 或 detach() 而直接銷毀線程對象,會導致程序崩潰。
t.join();
detach()
detach() 將線程與主線程分離,線程在后臺獨立運行,主線程不再等待它。
t.detach();
線程傳參
參數可以通過值傳遞給線程:
std::thread t(func, arg1, arg2);
線程同步與互斥
在多線程編程中,線程同步與互斥是兩個非常重要的概念,它們用于控制多個線程對共享資源的訪問,以避免數據競爭、死鎖等問題。
1. 互斥量(Mutex)
互斥量是一種同步原語,用于防止多個線程同時訪問共享資源。當一個線程需要訪問共享資源時,它首先需要鎖定(lock)互斥量。如果互斥量已經被其他線程鎖定,那么請求鎖定的線程將被阻塞,直到互斥量被解鎖(unlock)。
#include <mutex>std::mutex mtx; // 全局互斥量void safeFunction() {mtx.lock(); // 請求鎖定互斥量// 訪問或修改共享資源mtx.unlock(); // 釋放互斥量
}int main() {std::thread t1(safeFunction);std::thread t2(safeFunction);t1.join();t2.join();return 0;
}
2. 鎖(Locks)
C++提供了多種鎖類型,用于簡化互斥量的使用和管理。
常見的鎖類型包括:
- std::lock_guard:作用域鎖,當構造時自動鎖定互斥量,當析構時自動解鎖。
- std::unique_lock:與std::lock_guard類似,但提供了更多的靈活性,例如可以轉移所有權和手動解鎖。
3. 條件變量(Condition Variable)
條件變量用于線程間的協調,允許一個或多個線程等待某個條件的發生。它通常與互斥量一起使用,以實現線程間的同步。
std::condition_variable用于實現線程間的等待和通知機制。
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void workerThread() {std::unique_lock<std::mutex> lk(mtx);cv.wait(lk, []{ return ready; }); // 等待條件// 當條件滿足時執行工作
}void mainThread() {{std::lock_guard<std::mutex> lk(mtx);// 準備數據ready = true;} // 離開作用域時解鎖cv.notify_one(); // 通知一個等待的線程
}
線程間通信
std::future 和 std::promise:實現線程間的值傳遞。
std::promise<int> p;
std::future<int> f = p.get_future();std::thread t([&p] {p.set_value(10); // 設置值,觸發 future
});int result = f.get(); // 獲取值