C/C++编程心得(三)
各類ptr
auto_ptr:它允許程序員創建一個指向某種資源的指針對象,當該對象離開它的作用域時,它所指向的資源也會被自動釋放。
在原本的C++中,new和delete必須配對使用,然而給每個異常處理分支添加delete是一件很麻煩的事。auto_ptr就是用來干這事的,它無需顯式調用delete。
對auto_ptr的賦值和拷貝會導致原來的auto_ptr變為NULL,從而無效化。
unique_ptr:auto_ptr是全局唯一的,且不采用引用計數管理,因此,賦值和拷貝都是不必要的,且會造成混淆。(尤其拷貝會導致原來的auto_ptr變為NULL,這個還能叫做拷貝嗎?)因此,auto_ptr在后續標準中,不再推薦使用,而是用unique_ptr替代。unique_ptr默認不支持賦值和拷貝,確需使用,要與std::move配合方可。否則會編譯出錯。
unique_ptr早先在boost庫中的時候,也叫做scoped_ptr。
shared_ptr:為多個擁有者管理內存中對象的生命周期而設計的。在你初始化一個shared_ptr后,你可以復制它,把函數參數的值遞給它,并把它分配給其它shared_ptr實例。所有實例指向同一個對象,并共享訪問一個“控制塊”,即每當一個新的shared_ptr被添加時,遞增和遞減引用計數,超出范圍,則復位。當引用計數到達零時,控制塊刪除內存資源和自身。
weak_ptr:如果對象A中有對象B的shared_ptr,而對象B中又有對象A的shared_ptr,那么就會出現循環引用的情況。這時可以使用weak_ptr。兩者的區別在于新建shared_ptr會增加引用計數,而weak_ptr不會。
上述指針還可以自己定義deleter:
void close_file(std::FILE* fp) { std::fclose(fp); } std::unique_ptr<std::FILE, decltype(&close_file)> fp(std::fopen("demo.txt", "r"),&close_file);上面的close_file就是自定義的deleter,用來進行特殊的析構。
上述ptr不僅可以保存對象的指針,還可以保存數組指針:
shared_ptr<double[]> p2( new double[n] );
此外ptr的聲明方式也有兩種(以unique_ptr為例):
-
方法1:std::unique_ptr<int>(new int(1));
-
方法2:std::make_unique<int>(1);
從實現來看,這些智能指針除了需要new對象之外,還需要new一個用于維護對象引用計數的控制塊,因此第2種方法一次性分配的效率會比較高。
一般來說,make_XXX在標準庫中,通常都帶有new對象和相關控制結構的操作,除了上述的make_unique之外,類似的還有make_pair等。
shared_ptr既然號稱共享,那自然有一定的共享規則在:
shared_ptr<int> sp1(new int(10)); shared_ptr<int> sp2(sp1), sp3; sp3 = sp1; //一個典型的錯誤用法 shared_ptr<int> sp4(sp1.get()); cout << sp1.use_count() << " " << sp2.use_count() << " " << sp3.use_count() << " " << sp4.use_count() << endl; //輸出 3 3 3 1sp1,sp2,sp3是相互關聯的共享指針,共同控制所指內存的生存期,sp4雖然指向同樣的內存,卻是與sp1,sp2,sp3獨立的,sp4按自己的引用計數來關聯內存的釋放。
auto p = std::make_unique<XXX>(); void f(XXX*); f(p); // error f(p.get()); // OK
雖然,類似p->YYY()的用法和普通指針一致,但是作為f函數參數的時候,就不行了。
參考:
https://mp.weixin.qq.com/s/32aeGOPaySjTmyKcjFKgYA
窺見C++11智能指針
https://www.cnblogs.com/wangkeqin/p/9351191.html
C++內存管理之shared_ptr
https://blog.csdn.net/River_Lethe/article/details/78734879
shared_ptr的使用和陷阱
decltype
和auto的用法類似,auto不僅要推導類型,還要定義變量,而decltype則只進行類型推導。
int add(int x,int y){return x+y; } int main(){double i=0;decltype(i) a; // doubledecltype(add()) b; //int 注意括號。不帶括號就是函數指針了。 }參考:
https://www.cnblogs.com/npbool/p/3433360.html
C++11初探:類型推導,auto和decltype
類型轉換
static_cast:基本等同于C語言的強制類型轉換,只有很小的差異。
dynamic_cast:主要用于類層次間的上行轉換和下行轉換。
const_cast:用于修改類型的const或volatile屬性。
reinterpret_cast:它可以把一個指針轉換成一個整數,也可以把一個整數轉換成一個指針。
上面4個算是C++的基本類型變換。除此之外還有一些變種。
- 和shared_ptr配套的類型變換:
static_pointer_cast:Static cast of shared_ptr
dynamic_pointer_cast:Dynamic cast of shared_ptr
const_pointer_cast:Const cast of shared_ptr
- dynamic_cast的改進版(主要是增加了一些斷言或者異常處理):
參考:
https://www.cnblogs.com/chenyangchun/p/6795923.html
C++強制類型轉換:static_cast、dynamic_cast、const_cast、reinterpret_cast
std::bind & std::placeholders
void f(int n1, int n2, int n3, const int& n4, int n5) {std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n'; }auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5); f2(10, 11, 12); // 進行到 f(12, g(12), 12, 4, 5); 的調用不定參數
int sum(int count, ...) {va_list vl;int sum = 0;va_start(vl, count);for (int i = 0; i < count; ++i){sum += va_arg(vl, int);}va_end(vl);return sum; }上面是C風格的變參函數,主要依賴了va_xx系列函數。
template<typename T> void Append(Optimizer::Optimizations& optimizations, T&& optimization) {optimizations.emplace_back(new T(optimization)); };template<typename Front, typename... Others> void Append(Optimizer::Optimizations& optimizations, Front&& front, Others&&... others) {Append<Front>(optimizations, std::forward<Front>(front));Append<Others...>(optimizations, std::forward<Others>(others)...); };template<typename... Args> Optimizer::Optimizations MakeOptimizations(Args&&... args) {Optimizer::Optimizations optimizations;Append(optimizations, std::forward<Args>(args)...);return optimizations; }上面是C++風格的變參函數。它主要采用了Parameter pack技術,簡單的說就是遞歸的模板展開。
以上面的函數為例:
1.首先使用第2個模板函數展開函數。
2.在第2個模板函數中調用第1個模板函數,而第1個模板函數不是變參函數,相當于是遞歸的結尾。
std::tuple、std::bind等的實現都借助了Parameter pack。
在C++17中,還提出一個叫做fold expression的技術。
template<typename ...Args> int sum(Args&&... args) { // return (args + ... + 1 * 2); // Error: operator with precedence below castreturn (args + ... + (1 * 2)); // OK }上述函數是一個對+運算符的fold expression。
參考:
https://www.jianshu.com/p/d22904f30930
C++11新特性–不定參數模板與std::tuple、std::bind實現原理
特化(traits)
template<class T1, class T2> // 普通版本,有兩個模板參數 class B { ..... };template<class T2> // 偏特化版本,指定其中一個參數,即指定了部分類型 class B<int , T2> { ..... };// 當實例化時的第一個參數為int 則會優先調用這個版本偏特化的條件:
1.必須有一個主模板。
2.模板類型被部分明確化。
相應的,如果模板參數全被指定,則為全特化。
對主版本模板類、全特化類、偏特化類的調用優先級從高到低進行排序是:全特化類>偏特化類>主版本模板類。
traits一方面,在面對不同的輸入類時,能找到合適的返回型別;另一方面,當型別對應有不同的實現函數的時候,能起到一個提取型別然后分流的作用。
標準庫已經內置了一些常見的traits操作,例如std::pointer_traits,該模板可返回迭代器指向對象的型別。
參考:
https://www.cnblogs.com/yyehl/p/7253254.html
C++ 模板偏特化-來自STL的思考
https://www.cnblogs.com/mangoyuan/p/6446046.html
C++ traits技術淺談
typename
vector<T>::size_type可能有三種解釋:
-
靜態數據成員
-
靜態成員函數
-
嵌套類型
前兩者比較好區分,函數名后一般有括號。但是嵌套類型就不好區分了,因此需要用typename關鍵字指定之。
參考:
https://blog.csdn.net/zhangxiao93/article/details/50569924
C++ typedef typename作用
typeid
typeid可用于打印類型名稱。這里的類型可以是固定類型,也可以是模板類型。
cout << typeid(int).name() << typeid(T).name() << endl;
總結
以上是生活随笔為你收集整理的C/C++编程心得(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C/C++编程心得(二)
- 下一篇: Ubuntu使用技巧(三), 硬盘安装L