一文詳解C++ 智能指針的原理、分類及使用
1. 智能指針介紹
為解決裸指針可能導(dǎo)致的內(nèi)存泄漏問題。如:
a)忘記釋放內(nèi)存;
b)程序提前退出導(dǎo)致資源釋放代碼未執(zhí)行到。
就出現(xiàn)了智能指針,能夠做到資源的自動(dòng)釋放。
2. 智能指針的原理和簡單實(shí)現(xiàn)
2.1 智能指針的原理
將裸指針封裝為一個(gè)智能指針類,需要使用該裸指針時(shí),就創(chuàng)建該類的對(duì)象;利用棧區(qū)對(duì)象出作用域會(huì)自動(dòng)析構(gòu)的特性,保證資源的自動(dòng)釋放。
2.2 智能指針的簡單實(shí)現(xiàn)
代碼示例:
template<typename T>
class MySmartPtr {
public:
MySmartPtr(T* ptr = nullptr):mptr(ptr) { // 創(chuàng)建該對(duì)象時(shí),裸指針會(huì)傳給對(duì)象
}
~MySmartPtr() { // 對(duì)象出作用域會(huì)自動(dòng)析構(gòu),因此會(huì)釋放裸指針指向的資源
delete mptr;
}
// *運(yùn)算符重載
T& operator*() { // 提供智能指針的解引用操作,即返回它包裝的裸指針的解引用
return *mptr;
}
// ->運(yùn)算符重載
T* operator->() { // 即返回裸指針
return mptr;
}
private:
T* mptr;
};
class Obj {
public:
void func() {
cout << "Obj::func" << endl;
}
};
void test01() {
/*創(chuàng)建一個(gè)int型的裸指針,
使用MySmartPtr將其封裝為智能指針對(duì)象ptr,ptr對(duì)象除了作用域就會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)。
智能指針就是利用棧上對(duì)象出作用域自動(dòng)析構(gòu)這一特性。*/
MySmartPtr<int> ptr0(new int);
*ptr0 = 10;
MySmartPtr<Obj> ptr1(new Obj);
ptr1->func();
(ptr1.operator->())->func(); // 等價(jià)于上面
/* 中間異常退出,智能指針也會(huì)自動(dòng)釋放資源。
if (xxx) {
throw "....";
}
if (yyy) {
return -1;
}
*/
}3. 智能指針分類
3.1 問題引入
接著使用上述自己實(shí)現(xiàn)的智能指針進(jìn)行拷貝構(gòu)造:
void test02() {
MySmartPtr<int> p1(new int); // p1指向一塊int型內(nèi)存空間
MySmartPtr<int> p2(p1); // p2指向p1指向的內(nèi)存空間
*p1 = 10; // 內(nèi)存空間的值為10
*p2 = 20; // 內(nèi)存空間的值被改為20
}但運(yùn)行時(shí)出錯(cuò):

原因在于p1和p2指向同一塊int型堆區(qū)內(nèi)存空間,p2析構(gòu)將該int型空間釋放,p1再析構(gòu)時(shí)釋放同一塊內(nèi)存,則出錯(cuò)。
那可否使用如下深拷貝解決該問題?
MySmartPtr(cosnt MySmartPtr<T>& src) {
mptr = new T(*src.mptr);
}不可以。因?yàn)榘凑章阒羔樀氖褂梅绞?,用戶本意是想將p1和p2都指向該int型堆區(qū)內(nèi)存,使用指針p1、p2都可改變?cè)搩?nèi)存空間的值,顯然深拷貝不符合此場(chǎng)景。
3.2 兩類智能指針
不帶引用計(jì)數(shù)的智能指針:只能有一個(gè)指針管理資源。
auto_ptr;
scoped_ptr;
unique_ptr;.
帶引用計(jì)數(shù)的智能指針:可以有多個(gè)指針同時(shí)管理資源。
shared_ptr;強(qiáng)智能指針。
weak_ptr: 弱智能指針。這是特例,不能控制資源的生命周期,不能控制資源的自動(dòng)釋放!
3.3 不帶引用計(jì)數(shù)的智能指針
只能有一個(gè)指針管理資源。
3.3.1 auto_ptr (不推薦使用)
void test03() {
auto_ptr<int> ptr1(new int);
auto_ptr<int> ptr2(ptr1);
*ptr2 = 20;
// cout << *ptr2 << endl; // 可訪問*ptr2
cout << *ptr1 << endl; //訪問*ptr1卻報(bào)錯(cuò)
}如上代碼,訪問*ptr1為何報(bào)錯(cuò)?
因?yàn)檎{(diào)用auto_ptr的拷貝構(gòu)造將ptr1的值賦值給ptr2后,底層會(huì)將ptr1指向nullptr;即將同一個(gè)指針拷貝構(gòu)造多次時(shí),只讓最后一次拷貝的指針管理資源,前面的指針全指向nullptr。
不推薦將auto_ptr存入容器。
3.3.2 scoped_ptr (使用較少)
scoped_ptr已將拷貝構(gòu)造函數(shù)和賦值運(yùn)算符重載delete了。
scoped_ptr(const scoped_ptr<T>&) = delete; // 刪除拷貝構(gòu)造 scoped_ptr<T>& operator=(const scoped_ptr<T>&) = delete; // 刪除賦值重載
3.3.3 unique_ptr (推薦使用)
unique_ptr也已將拷貝構(gòu)造函數(shù)和賦值運(yùn)算符重載delete。
unique_ptr(const unique_ptr<T>&) = delete; // 刪除拷貝構(gòu)造 unique_ptr<T>& operator=(const unique_ptr<T>&) = delete; // 刪除賦值重載
但unique_ptr提供了帶右值引用參數(shù)的拷貝構(gòu)造函數(shù)和賦值運(yùn)算符重載,如下:
void test04() {
unique_ptr<int> ptr1(new int);
// unique_ptr<int> ptr2(ptr1); 和scoped_ptr一樣無法通過編譯
unique_ptr<int> ptr2(std::move(ptr1)); // 但可使用move得到ptr1的右值類型
// *ptr1 也無法訪問
}3.4 帶引用計(jì)數(shù)的智能指針
可以有多個(gè)指針同時(shí)管理資源。
原理:給智能指針添加其指向資源的引用計(jì)數(shù)屬性,若引用計(jì)數(shù) > 0,則不會(huì)釋放資源,若引用計(jì)數(shù) = 0就釋放資源。
具體來說:額外創(chuàng)建資源引用計(jì)數(shù)類,在智能指針類中加入該資源引用計(jì)數(shù)類的指針作為其中的一個(gè)屬性;當(dāng)使用裸指針創(chuàng)建智能指針對(duì)象時(shí),創(chuàng)建智能指針中的資源引用計(jì)數(shù)對(duì)象,并將其中的引用計(jì)數(shù)屬性初始化為1,當(dāng)后面對(duì)該智能指針對(duì)象進(jìn)行拷貝(使用其他智能指針指向該資源時(shí))或時(shí),需要在其他智能指針對(duì)象類中將被拷貝的智能指針對(duì)象中的資源引用計(jì)數(shù)類的指針獲取過來,然后將引用計(jì)數(shù)+1;當(dāng)用該智能指針給其他智能指針進(jìn)行賦值時(shí),因?yàn)槠渌悄苤羔槺毁x值后,它們就不指向原先的資源了,原先資源的引用計(jì)數(shù)就-1,直至引用計(jì)數(shù)為0時(shí)delete掉資源;當(dāng)智能指針對(duì)象析構(gòu)時(shí),會(huì)使用其中的資源引用計(jì)數(shù)指針將共享的引用計(jì)數(shù)-1,直至引用計(jì)數(shù)為0時(shí)delete掉資源。
shared_ptr:強(qiáng)智能指針;可改變資源的引用計(jì)數(shù)。
weak_ptr:弱智能指針;不可改變資源的引用計(jì)數(shù)。
帶引用計(jì)數(shù)的智能指針的簡單實(shí)現(xiàn):
/*資源的引用計(jì)數(shù)類*/
template<typename T>
class RefCnt {
public:
RefCnt(T* ptr=nullptr):mptr(ptr) {
if (mptr != nullptr) {
mcount = 1; // 剛創(chuàng)建指針指針時(shí),引用計(jì)數(shù)初始化為1
}
}
void addRef() { // 增加引用計(jì)數(shù)
mcount++;
}
int delRef() { // 減少引用計(jì)數(shù)
mcount--;
return mcount;
}
private:
T* mptr; // 資源地址
int mcount; // 資源的引用計(jì)數(shù)
};
/*智能指針類*/
template<typename T>
class MySmartPtr {
public:
MySmartPtr(T* ptr = nullptr) :mptr(ptr) { // 創(chuàng)建該對(duì)象時(shí),裸指針會(huì)傳給對(duì)象
mpRefCnt = new RefCnt<T>(mptr);
}
~MySmartPtr() { // 對(duì)象出作用域會(huì)自動(dòng)析構(gòu),因此會(huì)釋放裸指針指向的資源
if (0 == mpRefCnt->delRef()) {
delete mptr;
mptr = nullptr;
}
}
// *運(yùn)算符重載
T& operator*() { // 提供智能指針的解引用操作,即返回它包裝的裸指針的解引用
return *mptr;
}
// ->運(yùn)算符重載
T* operator->() { // 即返回裸指針
return mptr;
}
// 拷貝構(gòu)造
MySmartPtr(const MySmartPtr<T>& src):mptr(src.mptr),mpRefCnt(src.mpRefCnt) {
if (mptr != nullptr) {
mpRefCnt->addRef();
}
}
// 賦值重載
MySmartPtr<T>& operator=(const MySmartPtr<T>& src) {
if (this == &src) // 防止自賦值
return *this;
/*若本指針改為指向src管理的資源,則本指針原先指向的資源的引用計(jì)數(shù)-1,
若原資源的引用計(jì)數(shù)為0,就釋放資源*/
if (0 == mpRefCnt->delRef()) {
delete mptr;
}
mptr = src.mptr;
mpRefCnt = src.mpRefCnt;
mpRefCnt->addRef();
return *this;
}
private:
T* mptr; // 指向資源的指針
RefCnt<T>* mpRefCnt; // 資源的引用計(jì)數(shù)
};強(qiáng)智能指針原理圖:
比如有如下創(chuàng)建強(qiáng)智能指針的語句:
shared_ptr<int> sp1(new int(10));
則如下所示:
(a)智能指針對(duì)象sp1中主要包括ptr指針指向其管理的資源,ref指針指向該資源的引用計(jì)數(shù),則顯然會(huì)開辟兩次內(nèi)存。
(b)uses為該資源的強(qiáng)智能指針的引用計(jì)數(shù),weaks為該資源的弱智能指針的引用計(jì)數(shù)。

3.4.1 shared_ptr
強(qiáng)智能指針??筛淖冑Y源的引用計(jì)數(shù)。
(1)強(qiáng)智能指針的交叉引用問題
class B;
class A {
public:
A() {
cout << "A()" << endl;
}
~A() {
cout << "~A()" << endl;
}
shared_ptr<B> _ptrb;
};
class B {
public:
B() {
cout << "B()" << endl;
}
~B() {
cout << "~B()" << endl;
}
shared_ptr<A> _ptra;
};
void test06() {
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());
pa->_ptrb = pb;
pb->_ptra = pa;
/*打印pa、pb指向資源的引用計(jì)數(shù)*/
cout << pa.use_count() << endl;
cout << pb.use_count() << endl;
}輸出結(jié)果:

可見pa、pb指向的資源的引用計(jì)數(shù)都為2,因此出了作用域?qū)е聀a、pb指向的資源都無法釋放,如下圖所示:

解決:
建議定義對(duì)象時(shí)使用強(qiáng)智能指針,引用對(duì)象時(shí)使用弱智能指針,防止出現(xiàn)交叉引用的問題。
什么是定義對(duì)象?什么是引用對(duì)象?
定義對(duì)象:
使用new創(chuàng)建對(duì)象,并創(chuàng)建一個(gè)新的智能指針管理它。
引用對(duì)象:
使用一個(gè)已存在的智能指針來創(chuàng)建一個(gè)新的智能指針。
定義對(duì)象和引用對(duì)象的示例如下:
shared_ptr<int> p1(new int()); // 定義智能指針對(duì)象p1 shared_ptr<int> p2 = make_shared<int>(10); // 定義智能指針對(duì)象p2 shared_ptr<int> p3 = p1; // 引用智能指針p1,并使用p3來共享它 weak_ptr<int> p4 = p2; // 引用智能指針p2,并使用p4來觀察它
如上述代碼,因?yàn)樵?strong>test06函數(shù)中使用pa對(duì)象的_ptrb引用pb對(duì)象,使用pb對(duì)象的_ptra引用pa對(duì)象,因此需要將A類、B類中的_ptrb和_ptra的類型改為弱智能指針weak_ptr即可,這樣就不會(huì)改變資源的引用計(jì)數(shù),能夠正確釋放資源。
3.4.2 weak_ptr
弱智能指針。不能改變資源的引用計(jì)數(shù)、不能管理對(duì)象生命周期、不能做到資源自動(dòng)釋放、不能創(chuàng)建對(duì)象,也不能訪問資源(因?yàn)閣eak_ptr未提供operator->和operator*運(yùn)算符重載),即不能通過弱智能指針調(diào)用函數(shù)、不能將其解引用。只能從一個(gè)已有的shared_ptr或weak_ptr獲得資源的弱引用。
弱智能指針weak_ptr若想用訪問資源,則需要使用lock方法將其提升為一個(gè)強(qiáng)智能指針,提升失敗則返回nullptr。(提升的情形常使用于多線程環(huán)境,避免無效的訪問,提升程序安全性)
注意:弱智能指針weak_ptr只能觀察資源的狀態(tài),但不能管理資源的生命周期,不會(huì)改變資源的引用計(jì)數(shù),不能控制資源的釋放。
weak_ptr示例:
void test07() {
shared_ptr<Boy> boy_sptr(new Boy());
weak_ptr<Boy> boy_wptr(boy_sptr);
// boy_wptr->study(); 錯(cuò)誤!無法使用弱智能指針訪問資源
cout << boy_sptr.use_count() << endl; // 引用計(jì)數(shù)為1,因?yàn)槿踔悄苤羔槻桓淖円糜?jì)數(shù)
shared_ptr<int> i_sptr(new int(99));
weak_ptr<int> i_wptr(i_sptr);
// cout << *i_wptr << endl; 錯(cuò)誤!無法使用弱智能指針訪問資源
cout << i_sptr.use_count() << endl; // 引用計(jì)數(shù)為1,因?yàn)槿踔悄苤羔槻桓淖円糜?jì)數(shù)
/*弱智能指針提升為強(qiáng)智能指針*/
shared_ptr<Boy> boy_sptr1 = boy_wptr.lock();
if (boy_sptr1 != nullptr) {
cout << boy_sptr1.use_count() << endl; // 提升成功,引用計(jì)數(shù)為2
boy_sptr1->study(); // 可以調(diào)用
}
shared_ptr<int> i_sptr1 = i_wptr.lock();
if (i_sptr1 != nullptr) {
cout << i_sptr1.use_count() << endl; // 提升成功,引用計(jì)數(shù)為2
cout << *i_sptr1 << endl; // 可以輸出
}
}4. 智能指針與多線程訪問共享資源的安全問題
現(xiàn)要實(shí)現(xiàn)主線程創(chuàng)建子線程,讓子線程執(zhí)行打印Hello的函數(shù),有如下兩種方式:
方式1:主線程調(diào)用test08函數(shù),在test08函數(shù)中啟動(dòng)子線程執(zhí)行線程函數(shù),如下:
void handler() {
cout << "Hello" << endl;
}
void func() {
thread t1(handler);
}
int main(int argc, char** argv) {
func();
this_thread::sleep_for(chrono::seconds(1));
system("pause");
return 0;
}運(yùn)行報(bào)錯(cuò):

方式2:主線程中直接創(chuàng)建子線程來執(zhí)行線程函數(shù),如下:
void handler() {
cout << "Hello" << endl;
}
int main(int argc, char** argv) {
thread t1(handler);
this_thread::sleep_for(chrono::seconds(1));
system("pause");
return 0;
}運(yùn)行結(jié)果:無報(bào)錯(cuò)

上面兩種方式都旨在通過子線程調(diào)用函數(shù)輸出Hello,但為什么方式1報(bào)錯(cuò)?很簡單,不再贅述。
回歸本節(jié)標(biāo)題的正題,有如下程序:
class C {
public:
C() {
cout << "C()" << endl;
}
~C() {
cout << "~C()" << endl;
}
void funcC() {
cout << "C::funcC()" << endl;
}
private:
};
/*子線程執(zhí)行函數(shù)*/
void threadHandler(C* c) {
this_thread::sleep_for(chrono::seconds(1));
c->funcC();
}
/* 主線程 */
int main(int argc, char** argv) {
C* c = new C();
thread t1(threadHandler, c);
delete c;
t1.join();
return 0;
}運(yùn)行結(jié)果:

結(jié)果顯示c指向的對(duì)象被析構(gòu)了,但是仍然使用該被析構(gòu)的對(duì)象調(diào)用了其中的funcC函數(shù),顯然不合理。
因此在線程函數(shù)中,使用c指針訪問A對(duì)象時(shí),需要觀察A對(duì)象是否存活。
使用弱智能指針weak_ptr接收對(duì)象,訪問對(duì)象之前嘗試提升為強(qiáng)智能指針shared_ptr,提升成功則訪問,否則對(duì)象被析構(gòu)。
情形1:對(duì)象被訪問之前就被析構(gòu)了:
class C {
public:
C() {
cout << "C()" << endl;
}
~C() {
cout << "~C()" << endl;
}
void funcC() {
cout << "C::funcC()" << endl;
}
private:
};
/*子線程執(zhí)行函數(shù)*/
void threadHandler(weak_ptr<C> pw) { // 引用時(shí)使用弱智能指針
this_thread::sleep_for(chrono::seconds(1));
shared_ptr<C> ps = pw.lock(); // 嘗試提升
if (ps != nullptr) {
ps->funcC();
} else {
cout << "對(duì)象已經(jīng)析構(gòu)!" << endl;
}
}
/* 主線程 */
int main(int argc, char** argv) {
{
shared_ptr<C> p(new C());
thread t1(threadHandler, weak_ptr<C>(p));
t1.detach();
}
this_thread::sleep_for(chrono::seconds(5));
return 0;
}運(yùn)行結(jié)果:

情形2: 對(duì)象訪問完才被析構(gòu):
class C {
public:
C() {
cout << "C()" << endl;
}
~C() {
cout << "~C()" << endl;
}
void funcC() {
cout << "C::funcC()" << endl;
}
private:
};
/*子線程執(zhí)行函數(shù)*/
void threadHandler(weak_ptr<C> pw) { // 引用時(shí)使用弱智能指針
this_thread::sleep_for(chrono::seconds(1));
shared_ptr<C> ps = pw.lock(); // 嘗試提升
if (ps != nullptr) {
ps->funcC();
} else {
cout << "對(duì)象已經(jīng)析構(gòu)!" << endl;
}
}
/* 主線程 */
int main(int argc, char** argv) {
{
shared_ptr<C> p(new C());
thread t1(threadHandler, weak_ptr<C>(p));
t1.detach();
this_thread::sleep_for(chrono::seconds(5));
}
return 0;
}運(yùn)行結(jié)果:

可見shared_ptr與weak_ptr結(jié)合使用,能夠較好地保證多線程訪問共享資源的安全。
5.智能指針的刪除器deleter
刪除器是智能指針釋放資源的方式,默認(rèn)使用操作符delete來釋放資源。
但并非所有智能指針管理的資源都可通過delete釋放,如數(shù)組、文件資源、數(shù)據(jù)庫連接資源等。
有如下智能指針對(duì)象管理一個(gè)數(shù)組資源:
unique_ptr<int> ptr1(new int[100]);
此時(shí)再用默認(rèn)的刪除器則會(huì)造成資源泄露,因此需要自定義刪除器。
一些為部分自定義刪除器的示例:
/* 方式1:類模板 */
template<typename T>
class MyDeleter {
public:
void operator()(T* ptr) const {
cout << "數(shù)組自定義刪除器1." << endl;
delete[] ptr;
}
};
/* 方式2:函數(shù) */
void myDeleter(int* p) {
cout << "數(shù)組自定義刪除器2." << endl;
delete[] p;
}
void test09() {
unique_ptr<int, MyDeleter<int>> ptr1(new int[100]);
unique_ptr<int, void(*)(int*)> ptr2(new int[100], myDeleter);
/* 方式3:Lambda表達(dá)式 */
unique_ptr<int, void(*)(int*)> ptr3(new int[100], [](int* p) {
cout << "數(shù)組自定義刪除器3." << endl;
delete[] p;
});
}
void test10() {
unique_ptr<FILE, void(*)(FILE*)> ptr2(fopen("1.txt", "w"), [](FILE* f) {
cout << "文件自定義刪除器." << endl;
fclose(f);
});
}運(yùn)行結(jié)果:

6. make_shared和make_unique
6.1 make_shared
下面這種方式創(chuàng)建強(qiáng)智能指針存在缺陷。
shared_ptr<int> sp1(new int(10));
如下圖,假設(shè)為資源開辟內(nèi)存成功,但為引用計(jì)數(shù)結(jié)構(gòu)開辟內(nèi)存失敗,則shared_ptr創(chuàng)建sp1失敗,則不會(huì)去釋放new int(10)的資源,導(dǎo)致內(nèi)存泄漏。

因此,建議使用make_shared的方式創(chuàng)建強(qiáng)智能指針:
shared_ptr<int> sp1 = make_shared<int>(10); // 或者 auto sp1 = make_shared<int>(10); // 或者 auto sp1(make_shared<int>(10)); /*若有Test類,其中有兩個(gè)int型成員變量,則make_shared創(chuàng)建該類的智能指針的方式如下*/ shared_ptr<Test> sp2 = make_shared<Test>(1, 2); // 或者 auto sp2 = make_shared<Test>(1, 2); // 或者 auto sp2(make_shared<Test>(1, 2));
如下圖,make_shared創(chuàng)建智能指針時(shí),將資源的內(nèi)存和引用計(jì)數(shù)的內(nèi)存開辟在一起,因此只開辟一次內(nèi)存,要么開辟成功,要么開辟失敗,不存在像上面開辟兩次時(shí)可能導(dǎo)致內(nèi)存泄漏的問題。

因此make_shared的優(yōu)點(diǎn)有:
(a)內(nèi)存分配效率高;
(b)降低內(nèi)存泄漏風(fēng)險(xiǎn)。
但也存在缺點(diǎn):
(a)目前無法自定義刪除器;
(b)管理的內(nèi)存延遲釋放;
具體來說,原先的方式,無論弱引用計(jì)數(shù)weaks為多少,只要強(qiáng)引用計(jì)數(shù)uses為0,那塊int內(nèi)存就被釋放;注意:當(dāng)weaks和uses都為0時(shí),引用計(jì)數(shù)內(nèi)存才被釋放;
但由于make_shared的方式為資源內(nèi)存和引用計(jì)數(shù)開辟一整塊內(nèi)存,只要weaks和uses有不為0的,這一整塊內(nèi)存就不會(huì)被釋放。
6.2 make_unique
同樣的,建議使用make_unique。
優(yōu)點(diǎn):
(a)內(nèi)存分配效率高;
(b)降低內(nèi)存泄漏風(fēng)險(xiǎn)。
缺點(diǎn):
(a)目前無法自定義刪除器;
以上就是一文詳解C++ 智能指針的原理、分類及使用的詳細(xì)內(nèi)容,更多關(guān)于C++ 智能指針的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
pcl1.8.0+vs2013環(huán)境配置超詳細(xì)教程
這篇文章主要介紹了pcl1.8.0+vs2013環(huán)境配置超詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07
C語言常用標(biāo)準(zhǔn)頭文件使用實(shí)例
在C的系列語言程序中,頭文件(通常擴(kuò)展名為.h)被大量使用,它通常包含函數(shù)、變量、結(jié)構(gòu)體等的聲明和定義,以及一些宏定義和類型定義,這篇文章主要給大家介紹了關(guān)于C語言常用標(biāo)準(zhǔn)頭文件使用的相關(guān)資料,需要的朋友可以參考下2024-08-08
C語言 詳細(xì)解析時(shí)間復(fù)雜度與空間復(fù)雜度
算法復(fù)雜度分為時(shí)間復(fù)雜度和空間復(fù)雜度。其作用: 時(shí)間復(fù)雜度是度量算法執(zhí)行的時(shí)間長短;而空間復(fù)雜度是度量算法所需存儲(chǔ)空間的大小2022-04-04
C語言實(shí)現(xiàn)學(xué)生管理系統(tǒng)總結(jié)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
全面了解#pragma once與 #ifndef的區(qū)別
下面小編就為大家?guī)硪黄媪私?pragma once與 #ifndef的區(qū)別。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-08-08
C++ Log日志類輕量級(jí)支持格式化輸出變量實(shí)現(xiàn)代碼
這篇文章主要介紹了C++ Log日志類輕量級(jí)支持格式化輸出變量實(shí)現(xiàn)代碼,需要的朋友可以參考下2019-04-04

