C++進(jìn)階異常處理與智能指針實戰(zhàn)指南
C++ 進(jìn)階:異常處理與智能指針實戰(zhàn)指南
在 C++ 開發(fā)中,“錯誤處理” 與 “資源管理” 是兩大核心痛點。傳統(tǒng) C 語言的錯誤處理方式繁瑣且脆弱,而手動管理內(nèi)存又容易因異常導(dǎo)致泄漏。本文將從異常機(jī)制入手,逐步過渡到智能指針,帶你掌握 C++ 中可靠編程的關(guān)鍵技術(shù),解決 “如何優(yōu)雅處理錯誤” 與 “如何安全管理資源” 兩大難題。
一、C++ 異常:告別錯誤碼的優(yōu)雅解決方案
在 C 語言中,我們習(xí)慣用assert終止程序或返回錯誤碼處理問題,但這些方式存在明顯局限。C++ 異常機(jī)制的出現(xiàn),讓錯誤處理更靈活、信息更完整。
1.1 先看 C 語言錯誤處理的痛點
C 語言處理錯誤的兩種核心方式,都有難以規(guī)避的缺陷:
- 終止程序(如 assert):過于粗暴,用戶無法接受(比如內(nèi)存錯誤直接崩潰);
- 返回錯誤碼:需要手動檢查每個函數(shù)返回值,深層調(diào)用鏈中需 “層層傳遞” 錯誤,代碼冗余且易遺漏。
舉個例子,若ConnectSql函數(shù)返回錯誤碼,調(diào)用鏈需逐層傳遞才能讓外層處理:
// C語言風(fēng)格:錯誤碼層層傳遞的冗余
int ConnectSql() {
if (權(quán)限不足) return 1;
if (連接失敗) return 2;
return 0;
}
int ServerStart() {
int ret = ConnectSql();
if (ret != 0) return ret; // 手動傳遞錯誤碼
int fd = socket();
if (fd < 0) return errno; // 再傳遞系統(tǒng)錯誤碼
}
int main() {
int ret = ServerStart();
if (ret != 0) {
// 還需根據(jù)錯誤碼查表判斷具體問題
printf("錯誤碼:%d\n", ret);
}
return 0;
}1.2 異常的基本用法:try/throw/catch
C++ 通過try(保護(hù)代碼)、throw(拋出異常)、catch(捕獲異常)三段式處理錯誤,核心邏輯是 “哪里出錯拋哪里,哪里能處理哪里接”。
語法框架
try {
// 可能拋出異常的“保護(hù)代碼”
函數(shù)調(diào)用或危險操作;
} catch (異常類型1 e1) {
// 處理類型1的異常
} catch (異常類型2 e2) {
// 處理類型2的異常
} catch (...) {
// 捕獲所有未匹配的異常(兜底,避免程序崩潰)
}實戰(zhàn)示例:除 0 錯誤處理
用異常重構(gòu) “除 0 錯誤”,無需層層傳遞錯誤碼:
#include <iostream>
using namespace std;
// 發(fā)生除0時拋出異常
double Division(int a, int b) {
if (b == 0) {
// 拋出字符串異常(也可拋自定義對象)
throw "Division by zero condition!";
}
return (double)a / b;
}
void Func() {
int len, time;
cin >> len >> time;
// 若Division拋異常,直接跳轉(zhuǎn)到catch
cout << Division(len, time) << endl;
}
int main() {
try {
Func();
} catch (const char* errmsg) {
// 捕獲字符串類型異常,打印錯誤信息
cout << "錯誤:" << errmsg << endl;
} catch (...) {
// 兜底:捕獲所有其他類型異常
cout << "未知異常" << endl;
}
return 0;
}1.3 異常的核心規(guī)則:必須掌握的細(xì)節(jié)
異常的拋出與捕獲并非 “隨便匹配”,需遵守以下規(guī)則,否則易導(dǎo)致程序崩潰:
(1)匹配原則:類型決定捕獲邏輯
- 異常對象的類型決定了哪個
catch會被激活; - 允許 “派生類對象拋,基類捕獲”(實戰(zhàn)中常用此特性設(shè)計異常體系);
catch(...)是 “萬能捕獲”,但無法獲取異常具體信息,需謹(jǐn)慎使用。
(2)棧展開:從拋出點找捕獲點
若throw不在try內(nèi),或try后無匹配的catch,會觸發(fā) “棧展開”:
- 退出當(dāng)前函數(shù)棧,回到調(diào)用者的棧幀;
- 重復(fù)檢查調(diào)用者的
try/catch,直到找到匹配的catch; - 若一直找到
main函數(shù)仍無匹配,程序直接終止。
(3)異常重新拋出:部分處理后移交外層
若單個catch無法完全處理異常(比如僅釋放資源),可通過throw;重新拋出,讓外層處理:
void Func() {
// 申請資源(若拋異常需釋放)
int* array = new int[10];
try {
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
} catch (...) {
// 先釋放資源,再重新拋出異常
cout << "釋放array:" << array << endl;
delete[] array;
throw; // 移交外層處理
}
// 正常執(zhí)行時釋放資源
delete[] array;
}
1.4 異常安全:那些不能踩的坑
異常雖好,但若使用不當(dāng),會導(dǎo)致資源泄漏、對象不完整等問題,核心注意兩點:
(1)構(gòu)造函數(shù):盡量不拋異常
構(gòu)造函數(shù)負(fù)責(zé)對象初始化,若中途拋異常,對象可能處于 “半初始化” 狀態(tài)(部分成員已創(chuàng)建,部分未創(chuàng)建),導(dǎo)致資源泄漏。
(2)析構(gòu)函數(shù):絕對不拋異常
析構(gòu)函數(shù)負(fù)責(zé)資源清理(如delete、關(guān)閉文件),若拋異常:
- 若已有一個異常在處理中,會直接終止程序;
- 可能導(dǎo)致資源未釋放(如
delete未執(zhí)行)。
1.5 實戰(zhàn):自定義異常體系(企業(yè)級規(guī)范)
實際項目中,若隨意拋int、string等零散類型,外層無法統(tǒng)一處理。規(guī)范做法是設(shè)計繼承式異常體系:定義一個基類,所有具體異常繼承自它,外層只需捕獲基類即可。
企業(yè)級異常體系示例
#include <string>
using namespace std;
// 異?;?
class Exception {
public:
Exception(const string& errmsg, int id)
: _errmsg(errmsg), _id(id) {}
// 虛函數(shù):支持多態(tài),子類重寫錯誤信息
virtual string what() const {
return _errmsg;
}
protected:
string _errmsg; // 錯誤描述
int _id; // 錯誤編號(便于定位問題)
};
// SQL異常(派生類)
class SqlException : public Exception {
public:
SqlException(const string& errmsg, int id, const string& sql)
: Exception(errmsg, id), _sql(sql) {}
// 重寫what,添加SQL信息
virtual string what() const override {
string str = "SqlException: ";
str += _errmsg;
str += " (SQL: ";
str += _sql;
str += ")";
return str;
}
private:
string _sql; // 出問題的SQL語句
};
// 緩存異常(派生類)
class CacheException : public Exception {
public:
CacheException(const string& errmsg, int id)
: Exception(errmsg, id) {}
virtual string what() const override {
return "CacheException: " + _errmsg;
}
};
// 模擬SQL操作:隨機(jī)拋異常
void SQLMgr() {
srand(time(0));
if (rand() % 7 == 0) {
throw SqlException("權(quán)限不足", 100, "select * from user where name='張三'");
}
}
// 模擬緩存操作:隨機(jī)拋異常
void CacheMgr() {
srand(time(0));
if (rand() % 5 == 0) {
throw CacheException("數(shù)據(jù)不存在", 101);
}
SQLMgr(); // 調(diào)用SQL操作
}
int main() {
while (1) {
this_thread::sleep_for(chrono::seconds(1));
try {
CacheMgr();
cout << "操作成功" << endl;
} catch (const Exception& e) {
// 捕獲基類,統(tǒng)一處理所有異常(多態(tài)生效)
cout << "捕獲異常:" << e.what() << endl;
} catch (...) {
cout << "未知異常" << endl;
}
}
return 0;
}優(yōu)勢
- 外層只需一個
catch(const Exception& e),即可處理所有派生類異常; - 錯誤信息結(jié)構(gòu)化(含錯誤編號、SQL 語句等),便于定位問題;
- 擴(kuò)展性強(qiáng):新增異常類型只需繼承基類,無需修改外層捕獲邏輯。
1.6 標(biāo)準(zhǔn)庫異常體系:了解即可
C++ 標(biāo)準(zhǔn)庫定義了一套異常體系(頭文件<exception>),所有異常繼承自std::exception,但實際中很少直接使用 —— 因為設(shè)計較簡單,無法滿足復(fù)雜業(yè)務(wù)需求(如無法攜帶錯誤編號、SQL 語句等)。
核心繼承關(guān)系:
std::exception ├─ std::bad_alloc(new失敗時拋) ├─ std::bad_cast(dynamic_cast失敗時拋) ├─ std::logic_error(邏輯錯誤,如參數(shù)無效) │ ├─ std::invalid_argument(無效參數(shù)) │ └─ std::out_of_range(越界,如vector::at) └─ std::runtime_error(運(yùn)行時錯誤) └─ std::overflow_error(算術(shù)溢出)
使用示例(捕獲vector越界異常):
#include <vector>
#include <exception>
int main() {
try {
vector<int> v(10);
v.at(10) = 100; // 越界,拋out_of_range
} catch (const exception& e) {
// 調(diào)用what()獲取錯誤信息
cout << e.what() << endl; // 輸出:vector::_M_range_check: __n (which is 10) >= this->size() (which is 10)
}
return 0;
}1.7 異常的優(yōu)缺點總結(jié)
| 優(yōu)點 | 缺點 |
|---|---|
| 錯誤信息完整(可攜帶上下文,如 SQL 語句) | 執(zhí)行流跳轉(zhuǎn)混亂,調(diào)試難度增加 |
| 無需層層傳遞錯誤碼,代碼更簡潔 | 存在輕微性能開銷(現(xiàn)代硬件可忽略) |
| 支持構(gòu)造函數(shù) / 運(yùn)算符重載等無返回值場景 | 易導(dǎo)致資源泄漏(需配合智能指針解決) |
| 兼容第三方庫(如 boost、gtest) | 標(biāo)準(zhǔn)庫異常體系不實用,需自定義 |
結(jié)論:異常利大于弊,是 C++ 錯誤處理的主流方案,關(guān)鍵是配合智能指針解決資源泄漏問題。
二、智能指針:解決異常資源泄漏的 “神器”
異常會導(dǎo)致代碼執(zhí)行流跳變,若new后拋異常,delete可能無法執(zhí)行,進(jìn)而引發(fā)內(nèi)存泄漏。智能指針的出現(xiàn),正是通過RAII 思想,讓資源自動釋放。
2.1 先看異常引發(fā)的隱患:內(nèi)存泄漏
以下代碼中,若Func()拋異常,delete p會被跳過,導(dǎo)致內(nèi)存泄漏:
void Func() {
throw "模擬異常"; // 拋出異常
}
void Test() {
int* p = new int; // 申請內(nèi)存
Func(); // 拋異常,執(zhí)行流跳走
delete p; // 永遠(yuǎn)不會執(zhí)行,內(nèi)存泄漏
}內(nèi)存泄漏定義:程序分配內(nèi)存后,因設(shè)計錯誤失去對該內(nèi)存的控制,導(dǎo)致內(nèi)存無法復(fù)用,長期運(yùn)行會使程序響應(yīng)變慢甚至卡死。
2.2 RAII 思想:智能指針的基石
RAII(Resource Acquisition Is Initialization),即 “資源獲取即初始化”,核心邏輯是:
- 構(gòu)造時獲取資源:將資源(如內(nèi)存、文件句柄)綁定到對象的生命周期;
- 析構(gòu)時釋放資源:對象銷毀時,析構(gòu)函數(shù)自動釋放資源,無需手動調(diào)用。
基于 RAII 的簡單智能指針
template<class T>
class SmartPtr {
public:
// 構(gòu)造:獲取資源(內(nèi)存)
SmartPtr(T* ptr = nullptr) : _ptr(ptr) {}
// 析構(gòu):釋放資源(內(nèi)存)
~SmartPtr() {
if (_ptr) {
delete _ptr;
cout << "釋放內(nèi)存:" << _ptr << endl;
}
}
// 重載*和->,讓SmartPtr像普通指針一樣使用
T& operator*() { return *_ptr; }
T* operator->() { return _ptr; }
private:
T* _ptr; // 管理的資源(內(nèi)存地址)
};
// 測試:即使拋異常,內(nèi)存也會自動釋放
void Test() {
SmartPtr<int> sp(new int); // 構(gòu)造:獲取內(nèi)存
*sp = 10; // 像普通指針一樣使用
throw "模擬異常"; // 拋異常,sp對象銷毀時調(diào)用析構(gòu)
// 無需手動delete,析構(gòu)自動釋放
}核心優(yōu)勢
- 無需顯式釋放資源,避免人為遺漏;
- 即使發(fā)生異常,對象也會被銷毀(棧對象在棧展開時自動析構(gòu)),資源必然釋放。
三、C++ 智能指針全家桶:原理與實戰(zhàn)
C++ 標(biāo)準(zhǔn)庫提供了 4 種智能指針,分別應(yīng)對不同場景,其中auto_ptr已被淘汰,重點掌握unique_ptr、shared_ptr和weak_ptr。
3.1 auto_ptr:失敗的早期嘗試(避坑)
auto_ptr是 C++98 提供的第一個智能指針,采用 “管理權(quán)轉(zhuǎn)移” 機(jī)制,但存在嚴(yán)重缺陷,企業(yè)中已明確禁止使用。
缺陷:管理權(quán)轉(zhuǎn)移導(dǎo)致懸空指針
當(dāng)auto_ptr對象拷貝或賦值時,會轉(zhuǎn)移資源的管理權(quán),原對象變?yōu)?“懸空指針”(指向 nullptr),訪問原對象會崩潰:
#include <memory> // auto_ptr所在頭文件
int main() {
auto_ptr<int> sp1(new int(10));
auto_ptr<int> sp2 = sp1; // 管理權(quán)轉(zhuǎn)移:sp1失去資源,sp2擁有資源
*sp2 = 20; // 正常:sp2擁有資源
*sp1 = 30; // 崩潰:sp1已懸空(指向nullptr)
return 0;
}結(jié)論:永遠(yuǎn)不要使用auto_ptr,改用unique_ptr。
3.2 unique_ptr:獨(dú)占所有權(quán)的高效選擇
unique_ptr是 C++11 替代auto_ptr的方案,核心是獨(dú)占資源所有權(quán)—— 同一時間,只有一個unique_ptr能管理資源,禁止拷貝和賦值(直接刪除拷貝構(gòu)造和賦值運(yùn)算符)。
核心特性
- 禁止拷貝:
unique_ptr(const unique_ptr&) = delete; - 禁止賦值:
unique_ptr& operator=(const unique_ptr&) = delete; - 支持移動語義:可通過
std::move轉(zhuǎn)移所有權(quán)(轉(zhuǎn)移后原對象懸空)。
實戰(zhàn)示例
#include <memory>
int main() {
// 1. 基本使用
unique_ptr<int> sp1(new int(10));
cout << *sp1 << endl; // 10
// 2. 禁止拷貝和賦值(編譯報錯)
// unique_ptr<int> sp2 = sp1; // 錯誤:拷貝構(gòu)造已刪除
// sp1 = sp2; // 錯誤:賦值運(yùn)算符已刪除
// 3. 移動語義:轉(zhuǎn)移所有權(quán)
unique_ptr<int> sp2 = std::move(sp1); // 轉(zhuǎn)移后sp1懸空
cout << *sp2 << endl; // 10
// cout << *sp1 << endl; // 崩潰:sp1已懸空
// 4. 管理數(shù)組(需指定刪除器,或用unique_ptr<int[]>)
unique_ptr<int[]> sp3(new int[5]); // 專門用于數(shù)組,析構(gòu)時調(diào)用delete[]
sp3[0] = 1;
sp3[1] = 2;
return 0;
}適用場景
- 資源僅需一個所有者(如局部變量、函數(shù)返回值);
- 追求高效(無引用計數(shù)開銷,性能接近普通指針)。
3.3 shared_ptr:共享所有權(quán)的靈活方案
unique_ptr不支持拷貝,無法滿足 “多對象共享資源” 的場景(如多線程共享數(shù)據(jù))。shared_ptr通過引用計數(shù)實現(xiàn)共享所有權(quán),核心是 “記錄資源被多少對象引用,最后一個對象銷毀時釋放資源”。
核心原理
- 每個
shared_ptr管理一個資源和一個 “引用計數(shù)”(記錄共享該資源的shared_ptr數(shù)量); - 拷貝
shared_ptr時,引用計數(shù) + 1; shared_ptr銷毀時,引用計數(shù) - 1;- 若引用計數(shù)變?yōu)?0,釋放資源。
模擬實現(xiàn)核心代碼
template<class T>
class shared_ptr {
public:
// 構(gòu)造:資源+引用計數(shù)(初始為1)
shared_ptr(T* ptr = nullptr)
: _ptr(ptr), _pRefCount(new int(1)), _pmtx(new mutex) {}
// 拷貝構(gòu)造:引用計數(shù)+1
shared_ptr(const shared_ptr<T>& sp)
: _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx) {
AddRef(); // 引用計數(shù)+1(加鎖保證線程安全)
}
// 賦值運(yùn)算符:釋放當(dāng)前資源,引用新資源
shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
if (_ptr != sp._ptr) { // 避免自賦值
Release(); // 釋放當(dāng)前資源(引用計數(shù)-1,為0則刪除)
_ptr = sp._ptr;
_pRefCount = sp._pRefCount;
_pmtx = sp._pmtx;
AddRef(); // 新資源引用計數(shù)+1
}
return *this;
}
// 析構(gòu):引用計數(shù)-1,為0則釋放資源
~shared_ptr() {
Release();
}
// 重載*和->
T& operator*() { return *_ptr; }
T* operator->() { return _ptr; }
// 獲取引用計數(shù)
int use_count() const { return *_pRefCount; }
private:
// 引用計數(shù)+1(加鎖,線程安全)
void AddRef() {
_pmtx->lock();
(*_pRefCount)++;
_pmtx->unlock();
}
// 引用計數(shù)-1,為0則釋放資源
void Release() {
_pmtx->lock();
bool needDelete = false;
if (--(*_pRefCount) == 0) {
delete _ptr;
delete _pRefCount;
needDelete = true;
}
_pmtx->unlock();
if (needDelete) {
delete _pmtx; // 最后一個對象銷毀時,刪除鎖
}
}
private:
T* _ptr; // 管理的資源
int* _pRefCount; // 引用計數(shù)(指針:所有共享對象共享同一計數(shù))
mutex* _pmtx; // 互斥鎖:保證引用計數(shù)操作線程安全
};實戰(zhàn)要點
- 線程安全:
- 引用計數(shù)的加減是線程安全的(內(nèi)部加鎖);
- 資源本身的訪問不是線程安全的(需用戶手動加鎖)。
- 自定義刪除器:
shared_ptr默認(rèn)用delete釋放資源,若資源是malloc分配的、數(shù)組或文件句柄,需自定義刪除器:
#include <cstdlib> // malloc/free
#include <cstdio> // FILE/fclose
// 1. 管理malloc分配的內(nèi)存(自定義刪除器)
void FreeFunc(int* ptr) {
free(ptr);
cout << "free內(nèi)存:" << ptr << endl;
}
shared_ptr<int> sp1((int*)malloc(4), FreeFunc);
// 2. 管理數(shù)組(用lambda作為刪除器)
shared_ptr<int> sp2(new int[5], [](int* ptr) {
delete[] ptr;
cout << "delete[]數(shù)組:" << ptr << endl;
});
// 3. 管理文件句柄
shared_ptr<FILE> sp3(fopen("test.txt", "w"), [](FILE* ptr) {
fclose(ptr);
cout << "關(guān)閉文件:" << ptr << endl;
});3.4 weak_ptr:破解 shared_ptr 循環(huán)引用
shared_ptr存在一個致命問題:循環(huán)引用—— 兩個shared_ptr互相引用,導(dǎo)致引用計數(shù)無法歸零,資源永遠(yuǎn)無法釋放。
問題示例:雙向鏈表節(jié)點
struct ListNode {
int _data;
shared_ptr<ListNode> _prev; // 指向前驅(qū)節(jié)點
shared_ptr<ListNode> _next; // 指向后繼節(jié)點
~ListNode() { cout << "~ListNode()" << endl; }
};
int main() {
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl; // 1
cout << node2.use_count() << endl; // 1
node1->_next = node2; // node1的_next引用node2,node2計數(shù)變?yōu)?
node2->_prev = node1; // node2的_prev引用node1,node1計數(shù)變?yōu)?
// 析構(gòu)node1和node2:計數(shù)各減1,變?yōu)?(非0),資源不釋放
return 0;
}循環(huán)引用分析
node1和node2析構(gòu)時,引用計數(shù)從 2 減到 1(因_next和_prev仍互相引用);- 只有
_next和_prev析構(gòu)時,計數(shù)才會減到 0,但_next屬于node1,node1不釋放則_next不析構(gòu); - 最終形成 “死鎖”,資源永遠(yuǎn)無法釋放。
解決方案:用 weak_ptr 打破循環(huán)
weak_ptr是 “弱引用” 智能指針,特點是:
- 不增加引用計數(shù),僅觀察資源;
- 無法直接訪問資源(需先通過
lock()轉(zhuǎn)為shared_ptr)。
修改鏈表節(jié)點,將_prev和_next改為weak_ptr:
#include <memory>
struct ListNode {
int _data;
weak_ptr<ListNode> _prev; // 弱引用:不增加計數(shù)
weak_ptr<ListNode> _next; // 弱引用:不增加計數(shù)
~ListNode() { cout << "~ListNode()" << endl; }
};
int main() {
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl; // 1
cout << node2.use_count() << endl; // 1
node1->_next = node2; // weak_ptr賦值,node2計數(shù)仍為1
node2->_prev = node1; // weak_ptr賦值,node1計數(shù)仍為1
// 析構(gòu)node1和node2:計數(shù)減到0,資源釋放(打印~ListNode())
return 0;
}weak_ptr 的使用場景
- 打破
shared_ptr的循環(huán)引用(如雙向鏈表、樹結(jié)構(gòu)); - 觀察資源是否存在(通過
lock()判斷:若資源存在,返回非空shared_ptr;否則返回空)。
四、C++11 與 boost 智能指針的淵源
C++11 的智能指針并非憑空出現(xiàn),而是借鑒了boost庫的設(shè)計:
- C++98:僅提供
auto_ptr,設(shè)計缺陷明顯; - boost 庫:提出
scoped_ptr(獨(dú)占)、shared_ptr(共享)、weak_ptr(弱引用),解決了auto_ptr的問題; - C++ TR1:引入
shared_ptr,但非標(biāo)準(zhǔn); - C++11:正式納入
unique_ptr(對應(yīng)boost::scoped_ptr)、shared_ptr、weak_ptr,并優(yōu)化實現(xiàn)。
結(jié)論:C++11 智能指針是boost智能指針的 “標(biāo)準(zhǔn)化版本”,兼容性更好,無需額外依賴boost庫。
五、總結(jié):異常與智能指針的最佳實踐
- 異常使用規(guī)范:
- 定義繼承式異常體系,所有異常繼承自同一基類;
- 構(gòu)造函數(shù)盡量不拋異常,析構(gòu)函數(shù)絕對不拋異常;
- 外層用
catch(...)兜底,避免程序崩潰。
- 智能指針選擇優(yōu)先級:
- 優(yōu)先用
unique_ptr(獨(dú)占資源,高效無開銷); - 需共享資源時用
shared_ptr(注意循環(huán)引用,用weak_ptr解決); - 永遠(yuǎn)不用
auto_ptr。
- 資源管理原則:
- 內(nèi)存、文件句柄等資源,優(yōu)先用智能指針管理;
- 自定義資源(如網(wǎng)絡(luò)連接),用 RAII 思想封裝成類,讓資源自動釋放。
通過 “異常處理錯誤”+“智能指針管理資源”,可大幅提升 C++ 程序的可靠性和可維護(hù)性,這也是企業(yè)級 C++ 開發(fā)的核心技術(shù)之一。
到此這篇關(guān)于C++異常處理與智能指針實戰(zhàn)指南的文章就介紹到這了,更多相關(guān)C++異常與智能指針內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
數(shù)據(jù)結(jié)構(gòu) 數(shù)組順序存儲詳細(xì)介紹
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu) 數(shù)組順序存儲詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-05-05
Cocos2d-x UI開發(fā)之CCControlSwitch控件類使用實例
這篇文章主要介紹了Cocos2d-x UI開發(fā)之CCControlSwitch控件類使用實例,本文代碼中含大量注釋講解了CCControlSwitch控件類的使用,需要的朋友可以參考下2014-09-09

