C++11 智能指針的使?及其原理

目錄

1. 智能指針的使?場景分析

2. RAII和智能指針的設計思路

3. C++標準庫智能指針的使?

4. 智能指針的原理

5. shared_ptr和weak_ptr

5.1 shared_ptr循環引?問題

5.2 weak_ptr

6. shared_ptr的線程安全問題

7. C++11和boost中智能指針的關系

8. 內存泄漏

8.1 什么是內存泄漏,內存泄漏的危害

8.2?如何避免內存泄漏

1. 智能指針的使?場景分析

下?程序中我們可以看到,new了以后,我們也delete了,但是因為拋異常導,后?的delete沒有得到執?,所以就內存泄漏了,所以我們需要new以后捕獲異常,捕獲到異常后delete內存,再把異常拋出,但是因為new本?也可能拋異常,連續的兩個new和下?的Divide都可能會拋異常,讓我們處理起來很?煩。智能指針放到這樣的場景??就讓問題簡單多了。
#include <iostream>
using namespace std;
double Divide(int a, int b)
{// 當b == 0時拋出異常if (b == 0){throw "Divide by zero condition!";}else{return (double)a / (double)b;}
}
void Func()
{// 這?可以看到如果發?除0錯誤拋出異常,另外下?的array1和array2沒有得到釋放。// 所以這?捕獲異常后并不處理異常,異常還是交給外?處理,這?捕獲了再重新拋出去。// 但是如果array2new的時候拋異常呢,就還需要套?層捕獲釋放邏輯,這?更好解決?案// 是智能指針,否則代碼太挫了int* array1 = new int[10];int* array2 = new int[10]; // 拋異常呢?try{int len, time;cin >> len >> time;cout << Divide(len, time) << endl;}catch (...){cout << "delete []" << array1 << endl;cout << "delete []" << array2 << endl;delete[] array1;delete[] array2;throw; // 異常重新拋出,捕獲到什么拋出什么}// ...cout << "delete []" << array1 << endl;delete[] array1;cout << "delete []" << array2 << endl;delete[] array2;
}
int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}catch (const exception& e){cout << e.what() << endl;}catch (...){cout << "未知異常" << endl;}return 0;
}

2. RAII和智能指針的設計思路

? RAII是Resource Acquisition Is Initialization的縮寫,它是?種管理資源的類的設計思想,本質是?種利?對象?命周期來管理獲取到的動態資源,避免資源泄漏,這?的資源可以是內存、?件指針、?絡連接、互斥鎖等等。RAII在獲取資源時把資源委托給?個對象,接著控制對資源的訪問,資源在對象的?命周期內始終保持有效,最后在對象析構的時候釋放資源,這樣保障了資源的正常釋放,避免資源泄漏問題。
? 智能指針類除了滿?RAII的設計思路,還要?便資源的訪問,所以智能指針類還會想迭代器類?樣,重載 operator*/operator->/operator[] 等運算符,?便訪問資源。
#include <iostream>
using namespace std;
template<class T>
class SmartPtr
{
public:// RAIISmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){cout << "delete[] " << _ptr << endl;delete[] _ptr;}// 重載運算符,模擬指針的?為,?便訪問資源T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T& operator[](size_t i){return _ptr[i];}
private:T* _ptr;
};
double Divide(int a, int b)
{// 當b == 0時拋出異常if (b == 0){throw "Divide by zero condition!";}else{return (double)a / (double)b;}
}
void Func()
{// 這?使?RAII的智能指針類管理new出來的數組以后,程序簡單多了SmartPtr<int> sp1 = new int[10];SmartPtr<int> sp2 = new int[10];for (size_t i = 0; i < 10; i++){sp1[i] = sp2[i] = i;}int len, time;cin >> len >> time;cout << Divide(len, time) << endl;
}
int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}catch (const exception& e){cout << e.what() << endl;}catch (...){cout << "未知異常" << endl;}return 0;
}

3. C++標準庫智能指針的使?

? C++標準庫中的智能指針都在<memory>這個頭?件下?,我們包含<memory>就可以是使?了,智能指針有好?種,除了weak_ptr它們都符合RAII和像指針?樣訪問的?為,原理上??主要是解決智能指針拷?時的思路不同。
? auto_ptr是C++98時設計出來的智能指針,它的特點是拷?時把被拷?對象的資源的管理權轉移給拷?對象,這是?個?常糟糕的設計,因為它會到被拷?對象懸空,產生訪問報錯的問題,C++11設計出新的智能指針后,強烈建議不要使?auto_ptr。其他C++11出來之前很多公司也是明令禁?使?這個智能指針的。
? unique_ptr是C++11設計出來的智能指針,它的名字翻譯出來是唯?指針,它的特點的不?持拷?,只?持移動。如果不需要拷?的場景就?常建議使?。
? shared_ptr是C++11設計出來的智能指針,它的名字翻譯出來是共享指針,它的特點是?持拷?,也?持移動。如果需要拷?的場景就需要使?它了。底層是?引?計數的?式實現的。
? weak_ptr是C++11設計出來的智能指針,它的名字翻譯出來是弱指針,它完全不同于上?的智能指針,他不?持RAII,也就意味著不能?它直接管理資源,weak_ptr的產?本質是要解決shared_ptr的?個循環引?導致內存泄漏的問題。具體細節下?我們再細講。
? 智能指針析構時默認是進?delete釋放資源,這也就意味著如果不是new出來的資源,交給智能指針管理,析構時就會崩潰。智能指針?持在構造時給?個刪除器,所謂刪除器本質就是?個可調?對象,這個可調?對象中實現你想要的釋放資源的?式,當構造智能指針時,給了定制的刪除器, 在智能指針析構時就會調?刪除器去釋放資源。因為new[]經常使?,所以為了簡潔?點, unique_ptr和shared_ptr都特化了?份[]的版本,使?時 unique_ptr<Date[]> up1(new Date[5]) ; shared_ptr<Date[]> sp1(new Date[5]); 就可以管理new []的資源。
? unique_ptr和shared_ptr?持刪除器的?式有所不同:unique_ptr是在類模板參數?持的,shared_ptr是構造函數參數?持的。
? template <class T, class... Args> shared_ptr<T> make_shared (Args&&... args);
? shared_ptr 除了?持?指向資源的指針構造,還?持make_shared ?初始化資源對象的值直接構造
? shared_ptr 和 unique_ptr 都?持了 operator bool的類型轉換,如果智能指針對象是?個空對象沒有管理資源,則返回false,否則返回true,意味著我們可以直接把智能指針對象給if判斷是否為空。
? shared_ptr 和 unique_ptr 都得構造函數都使?explicit修飾,防?普通指針隱式類型轉換成智能指針對象
#include <iostream>
#include <memory>
using namespace std;
struct Date
{int _year;int _month;int _day;Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){}~Date(){cout << "~Date()" << endl;}
};
int main()
{auto_ptr<Date> ap1(new Date);// 拷?時,管理權限轉移,被拷?對象ap1懸空auto_ptr<Date> ap2(ap1);// 空指針訪問error,ap1對象已經懸空//ap1->_year++;unique_ptr<Date> up1(new Date);// 不?持拷?//unique_ptr<Date> up2(up1);// ?持移動,但是移動后up1也懸空,所以使?移動要謹慎unique_ptr<Date> up3(move(up1));shared_ptr<Date> sp1(new Date);// ?持拷?shared_ptr<Date> sp2(sp1);shared_ptr<Date> sp3(sp2);//引用計數cout << sp1.use_count() << endl;sp1->_year++;cout << sp1->_year << endl;cout << sp2->_year << endl;cout << sp3->_year << endl;// ?持移動,但是移動后sp1也懸空,所以使?移動要謹慎shared_ptr<Date> sp4(move(sp1));return 0;
}

unique_ptr定制刪除器 建議仿函數 ;shared_ptr定制刪除器 都可以 相對建議lambda

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <memory>
using namespace std;
struct Date
{int _year;int _month;int _day;Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){}~Date(){cout << "~Date()" << endl;}
};
template<class T>
void DeleteArrayFunc(T* ptr)
{delete[] ptr;
}template<class T>
class DeleteArray
{
public:void operator()(T* ptr){delete[] ptr;}
};
class Fclose
{
public:void operator()(FILE* ptr){cout << "fclose:" << ptr << endl;fclose(ptr);}
};
int main()
{// 這樣實現程序會崩潰// unique_ptr<Date> up1(new Date[10]);// shared_ptr<Date> sp1(new Date[10]);// 解決?案1// 因為new[]經常使?,所以unique_ptr和shared_ptr// 實現了?個特化版本,這個特化版本析構時?的delete[]unique_ptr<Date[]> up1(new Date[5]);shared_ptr<Date[]> sp1(new Date[5]);// 解決?案2// 仿函數對象做刪除器// unique_ptr<Date, DeleteArray<Date>> up2(new Date[5], DeleteArray<Date>());// unique_ptr和shared_ptr?持刪除器的?式有所不同// unique_ptr是在類模板參數?持的,shared_ptr是構造函數參數?持的// 這?沒有使?相同的?式還是挺坑的// 使?仿函數unique_ptr可以不在構造函數傳遞,因為仿函數類型構造的對象直接就可以調?// 但是下?的函數指針和lambda的類型不可以unique_ptr<Date, DeleteArray<Date>> up2(new Date[5]);shared_ptr<Date> sp2(new Date[5], DeleteArray<Date>());// 函數指針做刪除器unique_ptr<Date, void(*)(Date*)> up3(new Date[5], DeleteArrayFunc<Date>);shared_ptr<Date> sp3(new Date[5], DeleteArrayFunc<Date>);// lambda表達式做刪除器auto delArrOBJ = [](Date* ptr) {delete[] ptr; };//decltype與auto關鍵字一樣,用于進行編譯時類型推導,//總是以一個普通表達式作為參數,返回該表達式的類型,//而且decltype并不會對表達式進行求值unique_ptr<Date, decltype(delArrOBJ)> up4(new Date[5], delArrOBJ);shared_ptr<Date> sp4(new Date[5], delArrOBJ);// 實現其他資源管理的刪除器shared_ptr<FILE> sp5(fopen("Test.cpp", "r"), Fclose());shared_ptr<FILE> sp6(fopen("Test.cpp", "r"), [](FILE* ptr) {cout << "fclose:" << ptr << endl;fclose(ptr);});return 0;
}
int main()
{shared_ptr<Date> sp1(new Date(2024, 9, 11));shared_ptr<Date> sp2 = make_shared<Date>(2024, 9, 11);auto sp3 = make_shared<Date>(2024, 9, 11);shared_ptr<Date> sp4;// if (sp1.operator bool())if (sp1)cout << "sp1 is not nullptr" << endl;if (!sp4)cout << "sp1 is nullptr" << endl;//shared_ptr 和 unique_ptr 都得構造函數都使?explicit修飾//防?普通指針隱式類型轉換成智能指針對象//不存在從"Date*"轉換到"std:shared_ptr<Date>"的適當構造函數//報錯shared_ptr<Date> sp5 = new Date(2024, 9, 11);//不存在從"Date*"轉換到"std:unique_ptr<Date,std::default_delete < Date >> "的適當構造函數unique_ptr<Date> sp6 = new Date(2024, 9, 11);return 0;
}

4. 智能指針的原理

? 下?我們模擬實現了auto_ptr和unique_ptr的核?功能,這兩個智能指針的實現?較簡單,我們了解?下原理即可。auto_ptr的思路是拷?時轉移資源管理權給被拷?對象,這種思路是不被認可的,也不建議使?。unique_ptr的思路是不?持拷?。
? 我們重點要看看shared_ptr是如何設計的,尤其是引?計數的設計,主要這??份資源就需要?個引?計數,所以引?計數采?靜態成員的?式是?法實現的,要使?堆上動態開辟的?式,構造智能指針對象時來?份資源,就要new?個引?計數出來。多個shared_ptr指向資源時就++引?計數,shared_ptr對象析構時就--引?計數引?計數減到0時代表當前析構的shared_ptr是最后?個管理資源的對象,則析構資源

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<memory>
#include<functional>
#include<atomic>
using namespace std;
struct Date
{int _year;int _month;int _day;Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){}~Date(){cout << "~Date()" << endl;}
};
namespace sy
{template<class T>class auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}//p2(p1)//拷?時把被拷?對象的資源的管理權轉移給拷?對象auto_ptr(auto_ptr<T>& sp):_ptr(sp._ptr){// 管理權轉移sp._ptr = nullptr;}// p1 = p2;auto_ptr<T>& operator=(auto_ptr<T>& ap){// 檢測是否為??給??賦值if (this != &ap){// 釋放當前對象中資源if (_ptr)delete _ptr;// 轉移ap中資源到當前對象中_ptr = ap._ptr;ap._ptr = NULL;}return *this;}~auto_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 像指針?樣使?T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};template<class T>class unique_ptr{public://explicit 防?普通指針隱式類型轉換成智能指針對象explicit unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr() {if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 像指針?樣使?T& operator*(){return *_ptr;}T* operator->(){return _ptr;}//不?持拷?,只?持移動unique_ptr(const unique_ptr<T>& sp) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;unique_ptr(unique_ptr<T>&& sp):_ptr(sp._ptr){sp._ptr = nullptr;}unique_ptr<T>& operator=(unique_ptr<T>&& sp){delete _ptr;_ptr = sp._ptr;sp._ptr = nullptr;}private:T* _ptr;};template<class T>class shared_ptr{public:explicit shared_ptr(T* ptr = nullptr): _ptr(ptr), _pcount(new atomic<int>(1)){}//刪除器template<class D>shared_ptr(T* ptr, D del): _ptr(ptr), _pcount(new atomic<int>(1)), _del(del){}//?持拷?,也?持移動shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _del(sp._del){++(*_pcount);}void release(){if (--(*_pcount) == 0){// 最后?個管理的對象,釋放資源_del(_ptr);delete _pcount;_ptr = nullptr;_pcount = nullptr;}}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){release();_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);_del = sp._del;}return *this;}~shared_ptr(){release();}T* get() const{return _ptr;}int use_count() const{return *_pcount;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;//int* _pcount;atomic<int>* _pcount; // 原子操作function<void(T*)> _del = [](T* ptr) {delete ptr; }; };template<class T>class weak_ptr{public:weak_ptr(){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}private:T * _ptr = nullptr;};
}int main()
{sy::auto_ptr<Date> ap1(new Date);// 拷?時,管理權限轉移,被拷?對象ap1懸空sy::auto_ptr<Date> ap2(ap1);// 空指針訪問,ap1對象已經懸空 error//ap1->_year++;sy::unique_ptr<Date> up1(new Date);// 不?持拷?//unique_ptr<Date> up2(up1);// ?持移動,但是移動后up1也懸空,所以使?移動要謹慎sy::unique_ptr<Date> up3(move(up1));sy::shared_ptr<Date> sp1(new Date);// ?持拷? 也支持移動sy::shared_ptr<Date> sp2(sp1);sy::shared_ptr<Date> sp3(sp2);cout << sp1.use_count() << endl;sp1->_year++;cout << sp1->_year << endl;cout << sp2->_year << endl;cout << sp3->_year << endl;return 0;
}

5. shared_ptr和weak_ptr

5.1 shared_ptr循環引?問題

? shared_ptr?多數情況下管理資源?常合適,?持RAII,也?持拷?。但是在循環引?的場景下會導致資源沒得到釋放內存泄漏,所以我們要認識循環引?的場景和資源沒釋放的原因,并且學會使?weak_ptr解決這種問題。
? 如下圖所述場景,n1和n2析構后,管理兩個節點的引?計數減到1。
1. 右邊的節點什么時候釋放呢,左邊節點中的_next管著,_next析構后,右邊的節點就釋放了。
2. _next什么時候析構呢,_next是左邊節點的的成員,左邊節點釋放,_next就析構了。
3. 左邊節點什么時候釋放呢,左邊節點由右邊節點中的_prev管著呢,_prev析構后,左邊的節點就釋放了。
4. _prev什么時候析構呢,_prev是右邊節點的成員,右邊節點釋放,_prev就析構了。
? ?此邏輯上成功形成回旋鏢似的循環引?,誰都不會釋放就形成了循環引?,導致內存泄漏。
? 把ListNode結構體中的_next和_prev改成weak_ptr,weak_ptr綁定到shared_ptr時不會增加它的引?計數,_next和_prev不參與資源釋放管理邏輯,就成功打破了循環引?,解決了這?的問題。

weak_ptr是專?綁定shared_ptr,不增加它的引?計數,作為?些場景的輔助管理。

struct ListNode
{int _data;/*std::shared_ptr<ListNode> _next;std::shared_ptr<ListNode> _prev;*/// 這?改成weak_ptr,當n1->_next = n2;綁定shared_ptr時// 不增加n2的引?計數,不參與資源釋放的管理,就不會形成循環引?了std::weak_ptr<ListNode> _next;std::weak_ptr<ListNode> _prev;~ListNode(){cout << "~ListNode()" << endl;}
};
int main()
{// 循環引? -- 內存泄露std::shared_ptr<ListNode> n1(new ListNode);std::shared_ptr<ListNode> n2(new ListNode);cout << n1.use_count() << endl;cout << n2.use_count() << endl;n1->_next = n2;n2->_prev = n1;cout << n1.use_count() << endl;cout << n2.use_count() << endl;// weak_ptr不?持管理資源,不?持RAII// weak_ptr是專?綁定shared_ptr,不增加他的引?計數,作為?些場景的輔助管理//std::weak_ptr<ListNode> wp(new ListNode);return 0;
}

5.2 weak_ptr

? weak_ptr不?持RAII,也不?持訪問資源,所以我們看?檔發現weak_ptr構造時不?持綁定到資源,只?持綁定到shared_ptr,綁定到shared_ptr時,不增加shared_ptr的引?計數,那么就可以解決上述的循環引?問題。
? weak_ptr也沒有重載operator*和operator->等,因為它不參與資源管理,那么如果它綁定的shared_ptr已經釋放了資源,那么它去訪問資源就是很危險的。weak_ptr?持expired檢查指向的資源是否過期use_count也可獲取shared_ptr的引?計數,weak_ptr想訪問資源時,可以調?lock(鎖)返回?個管理資源的shared_ptr,如果資源已經被釋放,返回的shared_ptr是?個空對象,如果資源沒有釋放,則通過返回的shared_ptr訪問資源是安全的。
//過期為非0 1
int main()
{std::shared_ptr<string> sp1(new string("111111"));std::shared_ptr<string> sp2(sp1);std::weak_ptr<string> wp = sp1;cout << wp.expired() << endl;cout << wp.use_count() << endl;// sp1和sp2都指向了其他資源,則weak_ptr就過期了sp1 = make_shared<string>("222222");cout << wp.expired() << endl;cout << wp.use_count() << endl;sp2 = make_shared<string>("333333");cout << wp.expired() << endl;cout << wp.use_count() << endl;wp = sp1;//std::shared_ptr<string> sp3 = wp.lock();auto sp3 = wp.lock();cout << wp.expired() << endl;cout << wp.use_count() << endl;*sp3 += "###";cout << *sp1 << endl;return 0;
}

6. shared_ptr的線程安全問題

? shared_ptr的引?計數對象在堆上,如果多個shared_ptr對象在多個線程中,進?shared_ptr的拷?析構時會訪問修改引?計數,就會存在線程安全問題,所以shared_ptr引?計數是需要加鎖或者原?操作保證線程安全的。
? shared_ptr指向的對象也是有線程安全的問題的,但是這個對象的線程安全問題不歸shared_ptr管,它也管不了,應該有外層使?shared_ptr的?進?線程安全的控制。
? 下?的程序會崩潰或者A資源沒釋放,sy::shared_ptr引?計數從int*改成atomic<int>*就可以保證引?計數的線程安全問題或者使?互斥鎖加鎖也可以。
#include<thread>
#include<mutex>
struct AA
{int _a1 = 0;int _a2 = 0;~AA(){cout << "~AA()" << endl;}
};
int main()
{sy::shared_ptr<AA> p(new AA);const size_t n = 100000;mutex mtx;auto func = [&](){for (size_t i = 0; i < n; ++i){// 這?智能指針拷?會++計數sy::shared_ptr<AA> copy(p);{unique_lock<mutex> lk(mtx);copy->_a1++;copy->_a2++;}}};thread t1(func);thread t2(func);t1.join();t2.join();cout << p->_a1 << endl;cout << p->_a2 << endl;cout << p.use_count() << endl;return 0;
}

7. C++11和boost中智能指針的關系

? Boost庫是為C++語?標準庫提供擴展的?些C++程序庫的總稱,Boost社區建?的初衷之?就是為C++的標準化?作提供可供參考的實現,Boost社區的發起?Dawes本?就是C++標準委員會的成員之?。在Boost庫的開發中,Boost社區也在這個?向上取得了豐碩的成果,C++11及之后的新語法和庫有很多都是從Boost中來的。
? C++ 98 中產?了第?個智能指針auto_ptr。
? C++ boost給出了更實?的scoped_ptr/scoped_array和shared_ptr/shared_array和weak_ptr等。
? C++ TR1,引?了shared_ptr等,不過注意的是TR1并不是標準版。
? C++ 11,引?了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr對應boost的scoped_ptr。并且這些智能指針的實現原理是參考boost中的實現的。

8. 內存泄漏

8.1 什么是內存泄漏,內存泄漏的危害

什么是內存泄漏:內存泄漏指因為疏忽或錯誤造成程序未能釋放已經不再使?的內存,?般是忘記釋放或者發?異常釋放程序未能執?導致的。內存泄漏并不是指內存在物理上的消失,?是應?程序分配某段內存后,因為設計錯誤,失去了對該段內存的控制,因?造成了內存的浪費。
內存泄漏的危害:普通程序運??會就結束了出現內存泄漏問題也不?,進程正常結束,?表的映射關系解除,物理內存也可以釋放。?期運?的程序出現內存泄漏,影響很?,如操作系統、后臺服務、?時間運?的客?端等等,不斷出現內存泄漏會導致可?內存不斷變少,各種功能響應越來越慢,最終卡死。
int main()
{// 申請?個1G未釋放,這個程序多次運?也沒啥危害// 因為程序?上就結束,進程結束各種資源也就回收了char* ptr = new char[1024 * 1024 * 1024];cout << (void*)ptr << endl;return 0;
}

8.2?如何避免內存泄漏

? ?程前期良好的設計規范,養成良好的編碼規范,申請的內存空間記著匹配的去釋放。ps:這個理想狀態。但是如果碰上異常時,就算注意釋放了,還是可能會出問題。需要下?條智能指針來管理才有保證。
? 盡量使?智能指針來管理資源,如果??場景?較特殊,采?RAII思想??造個輪?管理。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95584.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95584.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95584.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Linux】Linux進程狀態和僵尸進程:一篇看懂“進程在忙啥”

前言&#xff1a;歡迎各位光臨本博客&#xff0c;這里小編帶你直接手撕Make/Makefile (自動化構建)&#xff0c;文章并不復雜&#xff0c;愿諸君耐其心性&#xff0c;忘卻雜塵&#xff0c;道有所長&#xff01;&#xff01;&#xff01;&#xff01; **&#x1f525;個人主頁&a…

開源視頻剪輯工具推薦

開源視頻剪輯和處理工具近年來發展非常迅速&#xff0c;許多工具的功能已經足以媲美甚至超越一些商業軟件。以下是一些頂級的開源視頻編輯和處理工具&#xff0c;涵蓋了從簡單易用到專業級別的不同需求。 一、 主要視頻編輯軟件&#xff08;非線性編輯&#xff0c;NLE&#xf…

第十四屆藍橋杯青少組C++國賽[2023.5.28]第二部分編程題(4、 數獨填數)

參考程序&#xff1a;#include <bits/stdc.h> using namespace std;char board[9][9];// 檢查在 (r,c) 填 num 是否有效 bool isValid(int r, int c, char num) {for (int i 0; i < 9; i) {if (board[r][i] num) return false; // 同行if (board[i][c] num) r…

C語言中奇技淫巧08-使用alloca/__builtin_alloca從棧上分配空間

alloca是什么? alloca 是一個非標準但廣泛支持的 C 語言函數&#xff0c;用于在當前函數的棧&#xff08;stack&#xff09;上動態分配內存。 與 malloc 的區別&#xff1a; malloc 在堆&#xff08;heap&#xff09; 上分配內存&#xff0c;需要手動調用 free 釋放。alloca 在…

【Markdown轉Word完整教程】從原理到實現

Markdown轉Word完整教程&#xff1a;從原理到實現 前言 在技術文檔編寫和學術論文創作中&#xff0c;Markdown因其簡潔的語法和良好的可讀性而廣受歡迎。然而&#xff0c;在實際工作中&#xff0c;我們經常需要將Markdown文檔轉換為Word格式&#xff0c;以便與同事協作、提交正…

IBM穿孔卡片:現代計算技術的奠基之作

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; 1 打孔卡概述 穿孔卡片&#xff08;Punch Card&#xff09;又稱打孔卡…

亞馬遜旺季來臨如何用woot沖刺

在亞馬遜旺季來臨之際&#xff0c;使用Woot沖刺需結合新品推廣、老品激活、庫存清理等不同場景&#xff0c;通過精準選品、活動設置、廣告配合及數據監控實現銷量與排名的雙重提升。以下是具體操作指南&#xff1a;一、精準選品&#xff1a;匹配提報條件新品期選品標準&#xf…

AlexNet:計算機視覺的革命性之作

AlexNet: Revolutionizing Deep Learning for Computer Vision (1)網絡提出的背景 論文題目:ImageNet Classification with Deep Convolutional Neural Networks arXiv地址:https://arxiv.org/abs/1207.0575 在2012年ImageNet大規模視覺識別挑戰賽(ILSVRC)中,AlexNet以15…

【高等數學】第十一章 曲線積分與曲面積分——第二節 對坐標的曲線積分

上一節&#xff1a;【高等數學】第十一章 曲線積分與曲面積分——第一節 對弧長的曲線積分 總目錄&#xff1a;【高等數學】 目錄 文章目錄1. 對坐標的曲線積分的概念與性質1. 對坐標的曲線積分的概念與性質 變力沿曲線所作的功 先用曲線 LLL 上的點 M1(x1,y1),M2(x2,y2),…,M…

解析SQL Server核心服務與功能

SQL Server 安裝后會在 Windows 系統中注冊多個服務&#xff0c;每種服務負責不同的功能。主要服務類型包括&#xff1a; &#x1f4cc; 核心服務 (必須或常用)SQL Server Database Engine (數據庫引擎服務) 服務名稱格式&#xff1a; MSSQL$<InstanceName> (命名實例) 或…

專項智能練習(計算機動畫基礎)

1.小明在制作Flash作品時&#xff0c;舞臺及庫中素材如第下圖所示&#xff0c;把“馬”元件插入到“馬”圖層第1幀并放在舞臺的草地位置&#xff0c;發現舞臺中并無馬圖像顯示&#xff0c;下列情形中最有可能的是&#xff08; &#xff09;。A.“馬”圖層已被鎖定 B.“馬”圖層…

第三方庫集成:結合 Express.js 構建本地服務器

引言&#xff1a;Express.js 在 Electron 第三方庫集成中的本地服務器構建價值 在 Electron 框架的第三方庫集成生態中&#xff0c;Express.js 作為 Node.js 的經典 Web 框架&#xff0c;扮演著構建本地服務器的關鍵角色。它不僅僅是一個路由和中間件工具&#xff0c;更是 Elec…

百度地圖+vue+flask+爬蟲 推薦算法旅游大數據可視化系統Echarts mysql數據庫 帶沙箱支付+圖像識別技術

F012 百度地圖vueflask爬蟲 推薦算法旅游大數據可視化系統Echarts mysql數據庫 帶沙箱支付圖像識別技術 &#x1f4da;編號&#xff1a; F012 文章結尾部分有CSDN官方提供的學長 聯系方式名片 博主開發經驗15年,全棧工程師&#xff0c;專業搞定大模型、知識圖譜、算法和可視化…

# 開發中使用——鴻蒙CoreSpeechKit讓文字發聲后續

開發中使用——鴻蒙CoreSpeechKit讓文字發聲后續 設置音量大小 volume// 設置播報相關參數this.extraParam {"queueMode": 0, "speed": AppModel.speed, "volume": AppModel.volume, "pitch": 1, "languageContext": zh-CN,…

Java全棧開發面試實錄:從基礎到微服務的深度探索

Java全棧開發面試實錄&#xff1a;從基礎到微服務的深度探索 面試官與應聘者的初次見面 面試官&#xff1a;你好&#xff0c;很高興見到你。請先做個自我介紹吧。 應聘者&#xff1a;您好&#xff0c;我叫李明&#xff0c;今年28歲&#xff0c;是南京大學計算機科學與技術專業的…

前端路由切換不再白屏:React/Vue 實戰優化全攻略(含可運行 Demo)

摘要 在單頁應用&#xff08;SPA&#xff09;開發中&#xff0c;React、Vue、Angular 這些主流框架都依賴前端路由來完成頁面切換。好處是顯而易見的&#xff1a;首屏資源一次加載&#xff0c;后續頁面切換靠前端路由完成&#xff0c;體驗比傳統的多頁應用要順暢很多。 但是在實…

C#之LINQ

文章目錄前言LINQ一、LINQ1一、LINQ2一、LINQ3Where方法&#xff1a;每一項數據都會進過predicate的測試&#xff0c;如果針對一個元素&#xff0c;predicate執行的返回值為true&#xff0c;那么這個元素就會放到返回值中。獲取一條數據&#xff08;是否帶參數的兩種寫法&#…

第 2 講:Kafka Topic 與 Partition 基礎

課程概述 在第一篇課程中&#xff0c;我們了解了 Kafka 的基本概念和簡單的 Producer/Consumer 實現。 本篇課程將深入探討 Kafka 的核心機制&#xff1a;Topic 和 Partition。 學習目標 通過本課程&#xff0c;您將掌握&#xff1a; Topic 和 Partition 的設計原理&#x…

三階Bezier曲線曲率極值及對應的u的計算方法

三階&#xff08;三次&#xff09;Bezier曲線的曲率極值及其對應的參數 u 的計算是一個復雜的非線性優化問題。由于三階Bezier曲線是參數化曲線&#xff0c;其曲率表達式較為復雜&#xff0c;通常無法通過解析方法直接求得所有極值點&#xff0c;但可以通過求解曲率導數為零的方…

Unity:XML筆記(二)——Xml序列化、反序列化、IXmlSerializable接口

寫在前面&#xff1a;寫本系列(自用)的目的是回顧已經學過的知識、記錄新學習的知識或是記錄心得理解&#xff0c;方便自己以后快速復習&#xff0c;減少遺忘。三、Xml序列化序列化就是把想要存儲的內容轉換為字節序列用于存儲或傳遞。1、序列化我們先創建一個類&#xff0c;之…