智能指針學習筆記

轉載:http://www.cnblogs.com/wuchanming/p/4411878.html

1. 介紹

本文介紹智能指針的使用。智能指針是c++ 中管理資源的一種方式,用智能指針管理資源,不必擔心資源泄露,將c++ 程序員 從指針和內存管理中解脫出來,再者,這也是c++發展的趨勢(這話不是我說的,見《Effective c++》和《c++實踐編程》),應該認真學習一下。

智能指針中,最有名的應該數auto_ptr,該智能指針已經被納入標準庫,只需要包含<memory>頭文件即可以使用,另外,TR1文檔定義的shared_ptr和weak_ptr也已經實現(我用的gcc版本是gcc 4.6.1),它們的頭文件是<tr1/memory>?。除此之外,還有幾個好用的智能指針,不過它們屬于boost庫,不屬于STL ,所以,用不用得到,根據自己的需要。不過,了解一下總無妨,正所謂"功不唐捐"嘛。

下面分別介紹auto_ptr,scoped_ptr,scoped_array,shared_ptr,shared_array, weak_ptr 和 intrusive_ptr 。

2. auto_ptr

2.1 為什么要用智能指針

在介紹第一個智能指針之前,先介紹下為什么要使用智能指針。先看下面這個函數:

void f()
{ classA* ptr = new classA; // create an object explicitly ... // perform some operations delete ptr; // clean up(destory the object explicitly) }

這個函數是一系列麻煩的根源,一個顯而易見的問題是,我們經常忘了delete 動作,特別是當函數中間存在return 語句時更是如此。然而,真正的麻煩發生于更隱晦之處,那就是當異常發生時,我們所要面對的災難,異常一旦出現,函數將立刻退離,根本不會調用函數尾端的delete 語句。結果可能是內存遺失。防止這種資源遺失的常見辦法就是捕捉所有異常,例如:

void f()
{ classA* ptr = new classA; // create an object explicitly try{ ... // perform some operations } catch(...){ delete ptr; //-clean up throw;//-rethrow the exception } delete ptr; // clean up(destory the object explicitly) }

你看,為了異常發生時處理對象的刪除工作,程序代碼變得多么復雜和累贅!如果還有第二個對象,如果還需要更多的catch 子句,那么簡直是一場惡夢。這不是優良的編程風格,復雜而且容易出錯,必須盡力避免。

如果使用智能指針,情況就會大不相同了。這個智能指針應該保證,無論在何種情形下,只要自己被摧毀,就一定要連帶釋放其所指資源。而由于智能型指針本身就是局部變量,所以無論是正常退出還是異常退出,只要函數退出,它就一定會被銷毀。auto_ptr正是這種只能型指針。

2.2 auto_ptr

auto_ptr 是這樣一種指針:它是"它所指向的對象"的擁有者,所以,當身為對象擁有者的auto_ptr 被摧毀時,該對象也將遭到摧毀。auto_ptr 要求一個對象只能有一個擁有者,嚴禁一物二主。更詳細的說,?auto_ptr 管理的資源必須絕對沒有一個以上的auto_ptr 同時指向它。?這是因為資源的擁有者在銷毀的時候,會銷毀它所擁有的資源,資源不能釋放兩次,如果同時有兩個auto_ptr擁有同一個資源,那么,在第一個auto_ptr銷毀以后,第二個auto_ptr就成為野指針了,所以,任何時刻,一個資源只有一個擁有者

下面是上例改寫后的版本:

#include <iostream>
#include <memory> using namespace std; void f() { //create and initialize an auto_ptr std::auto_ptr<classA> ptr(new classA); ... //perform some operations }

不需要delete ,也不再需要catch 了。auto_ptr 的接口與一般指針非常相似:operator *用來提取其所指對象,operator-> 用來指向對象中的成員。然而,所有指針算法(包括++在內)都沒有定義。

注意,auto_ptr< >允許你使用一般指針慣用的賦值(assign)初始化方式。必須直接使用數值來完成除始化:

std::auto_ptr<classA> ptr1(new classA); //OK std::auto_ptr<classA> ptr2 = new classA;//ERROR

有了上面兩條語句,那么下面的問題就很顯然了。

std::auto_ptr<classA> ptr; // create an auto_ptr ptr = new classA; // ERROR ptr = std::auto_ptr<classA>(new classA); // ok, delete old object and own new

2.3 auto_ptr 擁有權的轉移

上面提到過,絕對沒有一個以上的auto_ptr同時指向同一個資源,那么,如果你復制(或賦值)一個auto_ptr指針會發生什么呢?發生擁有權轉移,如下所示:

//initizlize an auto_ptr with a new object
std::auto_ptr<classA> ptr1(new classA); //copy the auto_ptr std::auto_ptr<classA> ptr2(ptr1);

在第一個語句中,ptr擁有了那個new 出來的對象。在第二個語句中,擁有權由ptr1 轉移到ptr2,此后,ptr2擁有那個對象,ptr1則是一個空指針。

同理,賦值動作也會發生擁有權的轉移。

//initizlize an auto_ptr with a new object
std::auto_ptr<classA> ptr1(new classA); //copy the auto_ptr std::auto_ptr<classA> ptr2; ptr2 = ptr1;

在上面的語句中,如果ptr2已經擁有一個對象,則,賦值動作發生時,會調用delete,將該對象刪除。

因為auto_ptr 會發生擁有權轉移問題,所以,不能完全像使用普通指針一樣使用auto_ptr ,下面這個錯誤的用法演示的auto_ptr 的特性。

//this is a bad example
template <class T> void bad_print(std::auto_ptr<T> p)//p gets ownership of passed argument { //does p own an object? if (p.get() == NULL) { std::cout << "NULL"; } else { std::cout << *p; } //Oops,exiting delete the object to which p refers } int main(int argc, char const* argv[]) { std::auto_ptr<int> p(new int); *p = 42; // change value to which p refers bad_print(p); // Oops,deletes the memory to which p refers *p = 18; // RUNTIME ERROR return 0; }

我們只是想通過print函數,打印對象的值,可是,卻不小心把對象給銷毀了,這是非常低級的錯誤,再多用幾次auto_ptr 以后,就不會出現這種情況了。如果我們不是通過傳值,而是通過傳遞一個引用會怎么樣呢?可以這么做,可是,你得非常小心,千萬別在調用的函數里面將資源的擁有權轉移了。正確的用法應該聲明指針常量,如下所示:

const std::auto_ptr<int>p(new int); *p = 42 // change value to which p refers bad_print(p); // COMPILE-TIME ERROR *p = 18; // OK

注意,auto_ptr 是一個指針,const auto_ptr 要表達的意思是"指針常量,指針不可指向其他資源,但是指針所指之物可以修改",而不是指向常量的指針。所以,const auto_ptr 類似于?T* const p而不是指向常量的指針const T* p?。下面是一個使用auto_ptr 指針的完整示例:

#include <iostream>
#include <memory> using namespace std; //define output operator for auto_ptr //-print object value or NULL template<class T> ostream& operator<< (ostream &strm, const auto_ptr<T> &p) { //does p own an object ? if (p.get() == NULL) { strm << "NULL"; } else { strm << *p; } return strm; } int main(int argc, char* argv[]) { auto_ptr<int> p(new int(42)); auto_ptr<int> q; cout << "after initizlization:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl; q = p; cout << "after assigning auto pointers:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl; *q += 13; p = q; cout << "after change and reassignment" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl; return 0; }

輸出結果如下:

after initizlization:
p : 42 q : NULL after assigning auto pointers: p : NULL q : 42 after change and reassignment p : 55 q : NULL

2.4 auto_ptr 注意事項

  1. auto_ptr(以及后面介紹的std::tr1::shared_ptr) 在其析構函數內做delete,而不是delete[]動作,那意味著在動態分配而得到的array上使用auto_ptr(或tr1::shared_ptr)是一個餿主意。但是,這樣的代碼是可以通過編譯的,所以需要用戶自己留心。下面的代碼就會出現用new []分配資源,用delete而不是delete[] 釋放資源一樣的問題。

    std::auto_ptr<std::string> aps(new std::string[10]);//資源泄漏 std::tr1::shared_ptr<int> spi(new int[1024]); //資源泄漏
  2. 標準容器需要元素具有可復制和可賦值的特性,而復制和賦值操作會使auto_ptr 發生所有權轉移,所以,auto_ptr 不能存放在容器中

3. scoped_ptr

有了上面對auto_ptr 的解釋,理解scoped_ptr 就沒有什么難度了。scoped_ptr 的名字向讀者傳遞了明確的信息,這個智能指針只能在本作用域中使用,不希望被轉讓。 scoped_ptr 通過將拷貝構造函數和operator= 函數聲明為私有,以此阻止智能指針的復制,也就關閉了所有權轉移的大門

scoped_ptr 的用法與auto_ptr 幾乎一樣,大多數情況下它可以與auto_ptr 相互替換,它也可用從一個auto_ptr 獲得指針的管理權(同時,auto_ptr 失去管理權)

scoped_ptr 也具有與auto_ptr 同樣的"缺陷"——不能用作容器的元素,但原因不同,auto_ptr 是因為它的轉移語義,而scoped_ptr 則是因為不支持拷貝和賦值,不符合容器對元素類型的基本要求

下面的代碼演示了scoped_ptr 與auto_ptr 的區別。

auto_ptr<int> ap(new int(10)); // 一個auto_ptr<int> scoped_ptr<int> sp(ap); // 從auto_ptr 獲得原始指針 assert(ap.get() == 0); // 原auto_ptr 不再擁有指針 ap.reset(new int(20)); // auto_ptr 擁有新的指針 cout << *ap << "," << *sp << endl; auto_ptr<int> ap2; ap2 = ap; // ap2 從ap 獲得原始指針,發生所有權轉移 assert(ap.get() == 0); // ap 不再擁有指針 scoped_ptr<int> sp2; // 另一個scoped_ptr sp2 = sp; // 賦值操作,無法通過編譯

比起auto_ptr ,scoped_ptr 更明確的表達了代碼原始編寫者的意圖:只能在定義的作用域內使用,不可轉讓。

4. scoped_array

scoped_array 與scoped_ptr 沒什么區別,主要區別就是用 new[] 分配資源,用 delete [] 釋放資源,而scoped_ptr 用new 分配資源,用delete 釋放資源。用法如下:

#include <iostream>
#include <algorithm> #include <iterator> #include <boost/smart_ptr.hpp> using namespace std; using namespace boost; int main(int argc, char* argv[]) { int *arr = new int[100]; //動態分配資源 scoped_array<int> sa(arr);//用scoped_array 對象代理原始動態數組 //scoped_array<int> sa( new int[100]); fill_n(&sa[0], 100, 5); sa[10] = sa[20] + sa[30];//像普通數組一樣使用 cout << sa[10] << "\t" << sa[20] << endl; return 0;//在作用域最后,自動釋放資源 }

scoped_array 與 scoped_ptr 接口和功能幾乎一樣,主要區別如下:

  1. 構造函數接受的指針p 必須是new [] 的結果,而不能是new 表達式的結果。
  2. 沒有* , -> 操作符重載,因為scoped_array 持有的不是一個普通指針
  3. 析構函數使用delete []釋放資源,而不是delete
  4. 提供operator[] 操作符重載,可以像普通數組一樣使用下標訪問元素
  5. 沒有begin() end() 等類似容器的迭代器

上面這個例子,可以很方便的使用vector代替,《Boost 程序庫開發指南》的作者并不推薦使用scoped_array。

5. shared_ptr

5.1 shared_ptr 介紹

上面已經介紹了3種智能指針,如果按照重要程度排序,auto_ptr 是最重要的,其次應該算shared_ptr 了,shared_ptr 已經被納入標準庫了,用gcc 的用戶只需要#include<tr1/memory>用visual studio 08/10 的用戶通過加入頭文件#include<memory>即可。

shared_ptr 是一個最像指針的"智能指針",它實現了引用計數的功能,所以,指針可以隨意復制,在函數間傳遞,或者存儲在容器里面

shared_ptr 還有兩個特有的成員函數,分別是:

  1. unique() 用于檢查指針是否唯一的,如果是唯一的,就相當于auto_ptr
  2. use_count() 返回當前指針的引用計數,use_count() 不提供高效率的操作,所以,use_count() 應該僅僅用于測試或者調試。

下面看看shared_ptr 的用法:

#include <iostream>
#include <tr1/memory> #include <assert.h> using namespace std; int main(int argc, char* argv[]) { std::tr1::shared_ptr<int> sp( new int(10));//一個指向整數的shared_ptr assert( sp.unique());//現在shared_ptr 是指針的唯一持有者 std::tr1::shared_ptr<int> sp2 = sp;//第二個shared_ptr ,拷貝構造函數 //兩個shared_ptr 相等,指向同一個對象,且引用計數為2 assert(sp == sp2 && sp.use_count() == 2); *sp2 = 100;//使用解引用操作符修改被指對象 assert(*sp == 100);//另一個shared_ptr 同時也被修改 sp.reset(); assert(!sp);//sp 不再持有對象 return 0; }

再看一個復雜一點的例子,用以演示智能指針作為成員變量和函數參數的情況。

#include <iostream>
#include <tr1/memory> #include <assert.h> using namespace std; using namespace std::tr1; class shared{ private: shared_ptr<int> p; public: shared(shared_ptr<int> p_):p(p_){}; void print() { cout << "count:" << p.use_count() << " v = " << *p << endl; } }; void print_fun(shared_ptr<int> p) { cout << "count:" << p.use_count() << " v = " << *p << endl; } int main(int argc, char* argv[]) { shared_ptr<int> p(new int(100)); shared s1(p), s2(p); s1.print(); s2.print(); *p = 20; print_fun(p); s1.print(); }

輸出結果如下:

count:3 v = 100 count:3 v = 100 count:4 v = 20 count:3 v = 20

可以看到,我們不用關心shared_ptr 的具體實現,也不需要煩心它的引用計數是多少,我們只需要把它當成一個普通指針使用,再也不用擔心資源泄漏。

auto_ptr 不能一物侍二主,所以,拷貝的時候會發生所有權轉移,而shared_ptr 則不存在這個問題呢,那么,把一個 auto_ptr 復制給 shared_ptr 或者把一個shared_ptr 復制給auto_ptr 會發生什么呢?答案是編譯錯誤,即你不能這么做。

5.2 make_shared

前面說過,shared_ptr 是最像指針的智能指針,有了shared_ptr ,我們幾乎可以拋棄delete了,但是,我們還是用到了new,用到了new 而不delete ,很不對稱不是嗎,所以,TR1又定義了一個小工具make_shared(類似與make_pair)來幫助我們生成對象,不過,這個功能好像還沒有實現,如果,等不及要玩一下,可以用boost庫,make_shared 在頭文件#include<boost/make_shared.hpp>中定義,使用方法如下:

#include <iostream>
#include <string> #include <vector> #include <boost/make_shared.hpp> using namespace std; int main(int argc, char const* argv[]) { boost::shared_ptr<string> sp = boost::make_shared<string>("make_shared"); cout << *sp << endl; boost::shared_ptr< vector<int> > spv = boost::make_shared< vector<int> >(10, 2); cout << spv->size() << endl; return 0; }

shared_ptr 可以應用于標注庫,唯一需要牢記的是,shared_ptr 是一個指針,行為類似于普通指針,知道這一點以后,下面的代碼就不難理解了。

#include <boost/make_shared.hpp> #include <iostream> #include <vector> #include <boost/smart_ptr.hpp> using namespace std; using namespace boost; int main(int argc, char const* argv[]) { typedef vector< shared_ptr<int> > vs; //聲明一個持有shared_ptr 的標準容器類型,元素被初始化為空指針 vs v(10); int i = 0; for (vs::iterator pos = v.begin(); pos != v.end(); ++pos) { (*pos) = make_shared<int>(++i);//使用工廠函數(make_shared)賦值 cout << *(*pos) << ", ";//輸出值 } cout << endl; shared_ptr<int> p = v[9]; *p = 100; cout << *v[9] << endl; return 0; }

5.3 shared_ptr 的缺陷(循環引用)

shared_ptr 需要當心循環引用的問題,不然還是會發生資源泄漏。詳細信息見這里。

6. shared_array

我們知道scoped_ptr 和 scoped_array 的用法和區別以后,很容易猜到shared_array 的用法了。

shared_array 類似于shared_ptr ,它包裝了new[] 操作符在堆上分配的動態數組,同樣,使用引用計數機制為動態數組提供了一個代理,可以在程序的生命周期里上期存在,直到沒有任何引用后才釋放內存。

shared_array 的接口和功能幾乎與shared_ptr 是相同的,主要區別如下:

  1. 構造函數接受指針p必須是new[] 的結果,而不是new 分配的資源
  2. 提供operator[] 操作符重載,可以像普通數組一樣用下標訪問
  3. 沒有* -> 操作符重載,因為shared_array 持有的不是一個普通指針
  4. 析構函數使用delete[] 釋放資源,而不是delete

shared_array 用法的簡單示例:

#include <iostream>
#include <boost/smart_ptr.hpp> #include <assert.h> using namespace std; using namespace boost; int main(int argc, char const* argv[]) { shared_array<int> sa( new int[100]); shared_array<int> sa2 = sa; sa[0] = 10; cout << sa.use_count() << endl; cout << sa[0] << endl; assert( sa2[0] == 10); return 0; }

7. weak_ptr

關于weak_ptr 我是知其然,但不知其所以然。下面的說明和例子都來自《Boost 程序庫完全開發指南》,無任何更改,沒有理解透徹,怕改錯了。

weak_ptr 被設計為與shared_ptr 共同工作,可以從一個shared_ptr 或者另一個weak_ptr 對象構造,獲得資源的觀測權。但是weak_ptr 沒有共享資源,它的構造函數不會引起指針引用計數的增加。同樣,在weak_ptr 析構時,也不會導致引用計數減少,它只是一個靜靜的觀察者。

使用weak_ptr 的成員函數use_count() 可以觀測資源的引用計數,另一個成員函數expired() 的功能等價于use_count() == 0,但更快,表示被觀測的資源已經不復存在。

weak_ptr 沒有重載operator* 和 -> ,這是特意的,因為它不共享指針,不能操作資源,這正是它"弱"的原因,但它可以使用一個非常重要的成員函數lock() 從被觀測的shared_ptr 獲得一個可用的shared_ptr 對象,從而操作資源。但當expired() == ture的時候,lock()函數返回一個存儲空指針的shared_ptr 。

下面的代碼示范了weak_ptr 的用法:

shared_ptr<int> sp (new int(10)); // 一個shared_ptr assert(sp.use_count() == 1); weak_ptr<int> wp(sp); // 從shared_ptr 創建weak_ptr assert(sp.use_count() == 1); // weak_ptr 不影響引用計數 if (!wp.expired()) // 判斷weak_ptr 觀察的對象是否有效 { shared_ptr<int> sp2 = wp.lock();//獲得一個shared_ptr *sp2 = 100; assert(sp.use_count() == 2); }//退出作用域,sp2 自動析構,引用計數減1 assert(sp.use_count() == 1); sp.reset(); // shared_ptr 失效 assert(wp.expired()); // weak_ptr 將獲得一個空指針 assert(!wp.lock());

8. intrusive_ptr

Boost 庫實現了該指針,Boost 庫不推薦使用intrusive_ptr。

9. 注意事項

  • 在資源管理類中提供對原始資源的訪問(Item 15)

    使用智能指針的時候,可以通過get()成員函數,獲取原始指針,從而與一下需要用到原始資源的API打交道,如果這樣的API特別多,每次都寫.get() 不光費時,而且不夠清晰,這時,應該提供隱式類型轉換。如下所示:

      class Font{ public: .... //隱式類型轉換 operator FondHandle() const {return f;} ... };
  • 以獨立的語句將newd 對象置入智能指針(Item 17)

應該用獨立的語句將newd 的對象置入只能指針,考慮如下調用: processWidget(std::tr1::shared_ptr(new Widget), priority());

在上面的調用中,需要處理以下三件事:

  1. 調用priority
  2. 執行new Widget
  3. 調用tr1::shared_ptr構造函數

c++ 編譯器會以什么樣的次序完成上面三件事,我們不得而知,如果調用序列如下:

  1. 執行new Widget
  2. 調用priority
  3. 調用tr1::shared_ptr構造函數

萬一對priority 的調用導致異常,則new Widget返回的指針就會遺失,我們無法使用,也無法釋放該資源,所以,安全的處理方式,應該是這樣的:

std::tr1::shared_ptr<Widget> pw(new Widget); processWidget(pw, priority());

10. 總結

在上面所有介紹的智能指針中,auto_ptr ,shared_ptr 和weak_ptr 已經納入標準庫,可以放心使用,而不用擔心可移植性的問題。其中auto_ptr 和shared_ptr 最為重要,shared_ptr和普通指針最為相似,不知道該用哪種類型的智能指針的時候,就用shared_ptr 。

參考資料

  • 《C++ 標準程序庫》
  • 《Boost 程序庫完全開發指南》
  • 《Effective c++》

轉載:http://mingxinglai.com/cn/2013/01/smart-ptr/


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

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

相關文章

c++程序編譯過程

c程序編譯分成四個過程&#xff1a;編譯預處理&#xff0c;編譯&#xff0c;匯編&#xff0c;鏈接 編譯預處理&#xff1a;處理以#為開頭 編譯&#xff1a;將.cpp文件翻譯成.s匯編文件 匯編&#xff1a;將.s匯編文件翻譯成機器指令.o文件 鏈接&#xff1a;匯編生產的目標文件.o…

仿函數(函數對象)

轉載&#xff1a;http://www.cnblogs.com/wuchanming/p/4411867.html 本文乃作者學習《C標準程序庫》的學習筆記&#xff0c;首先介紹了仿函數&#xff08;函數對象&#xff09;和函數適配器&#xff08;配接器&#xff09;的概念&#xff0c;然后列出STL中所有的仿函數&#x…

C++ template —— 動多態與靜多態(六)

轉載&#xff1a;http://www.cnblogs.com/yyxt/p/5157517.html 前面的幾篇博文介紹了模板的基礎知識&#xff0c;并且也深入的講解了模板的特性。接下來的博文中&#xff0c;將會針對模板與設計進行相關的介紹。 ------------------------------------------------------------…

變量之間的區別

全局變量、局部變量、靜態全局變量、靜態局部變量的區別 c變量根據定義具有不同的生命周期&#xff0c;會有不同的作用域&#xff0c;主要有六個作用域&#xff1a;全局作用域&#xff0c;局部作用域&#xff0c;文件作用域&#xff0c;類作用域&#xff0c;語句作用域&#xf…

計算機的網絡體系以及參考模型

計算機的網絡體系以及參考模型一、OSI七層模型二、TCP/IP參考模型三、TCP/IP 五層參考模型四、OSI 模型和 TCP/IP 模型異同比較五、OSI 和 TCP/IP 協議之間的對應關系六、為什么 TCP/IP 去除了表示層和會話層&#xff1f;七、數據如何在各層之間傳輸&#xff08;數據的封裝過程…

C++ 模板詳解(二)

轉載&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html 四、類模板的默認模板類型形參 1、可以為類模板的類型形參提供默認值&#xff0c;但不能為函數模板的類型形參提供默認值。函數模板和類模板都可以為模板的非類型形參提供默認值。 2、類模板的類…

c++類對象的創建方式

對象創建限制在堆或棧 c類對象的創建方式對象創建限制在堆或棧C 中的類的對象的建立模式如何將類限制在堆上呢&#xff1f;C 中的類的對象的建立模式 C 中的類的對象的建立模式分為兩張&#xff1a;靜態建立&#xff0c;動態建立 靜態建立&#xff1a;由編譯器為對象在棧空間…

C++ 模板詳解(一)

轉載&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html C模板 模板是C支持參數化多態的工具&#xff0c;使用模板可以使用戶為類或者函數聲明一種一般模式&#xff0c;使得類中的某些數據成員或者成員函數的參數、返回值取得任意類型。 模板是一種對類…

劍指Offer09. 用兩個棧實現隊列

class CQueue { public:stack<int> stack1,stack2;CQueue() {//初始化棧while(!stack1.empty()){stack1.pop();}while(!stack2.empty()){stack2.pop();}}void appendTail(int value) {stack1.push(value);}int deleteHead() {if(stack2.empty()){while(!stack1.empty()){…

rk3588 之啟動

目錄 uboot版本配置修改編譯 linux版本配置修改編譯 啟動sd卡啟動制作spi 燒錄 參考 uboot 版本 v2024.01-rc2 https://github.com/u-boot/u-boot https://github.com/rockchip-linux/rkbin 配置修改 使用這兩個配置即可&#xff1a; orangepi-5-plus-rk3588_defconfig r…

C++引用詳解

轉載&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用的概念 引用&#xff1a;就是某一變量&#xff08;目標&#xff09;的一個別名&#xff0c;對引用的操作與對變量直接操作完全一樣。 引用的聲明方法&#xff1a;類型標識符 &引用名目標…

劍指Offer03.數組中重復的數字

找出數組中重復的數字。 在一個長度為 n 的數組 nums 里的所有數字都在 0&#xff5e;n-1 的范圍內。數組中某些數字是重復的&#xff0c;但不知道有幾個數字重復了&#xff0c;也不知道每個數字重復了幾次。請找出數組中任意一個重復的數字。 示例 1&#xff1a; 輸入&…

C++ 模板全特化中的函數特化

轉載&#xff1a;http://blog.csdn.net/rain_qingtian/article/details/15815251 [cpp] view plaincopy print?#include <iostream> using namespace std; template<typename T> bool isLess(T x, T y) { cout << "general version\n&q…

c++面向對象總結

c面向對象總結什么是面向對象&#xff1f;面向對象的三大特性重寫和重載的區別隱藏和重寫&#xff0c;重載的區別什么是多態&#xff1f;多態如何實現什么是面向對象&#xff1f;面向對象的三大特性 面向對象&#xff1a;對象是指具體的某一個事物&#xff0c;這些事物的抽象就…

類模板static成員的使用

轉載&#xff1a;http://blog.csdn.net/ljq32/article/details/7911390 1. 與普通類的static成員一樣&#xff0c;類內部聲明一次&#xff0c;類外部定義一次&#xff0c;定義時可以設置也可以不設置初始值; 2. 類模板內部聲明與普通類的static成員一致&#xff1a; [html] vi…

Linux網絡編程服務器模型選擇之循環服務器

轉載&#xff1a;http://www.cnblogs.com/lizhenghn/p/3617608.html 在網絡程序里面&#xff0c;通常都是一個服務器處理多個客戶機&#xff0c;為了出個多個客戶機的請求&#xff0c;服務器端的程序有不同的處理方式。本節開始介紹Linux下套接字編程的服務器模型選擇&#xff…

劍指Offer04. 二維數組中的查找

在一個 n * m 的二維數組中&#xff0c;每一行都按照從左到右遞增的順序排序&#xff0c;每一列都按照從上到下遞增的順序排序。請完成一個高效的函數&#xff0c;輸入這樣的一個二維數組和一個整數&#xff0c;判斷數組中是否含有該整數。 相當于二叉搜索樹,左孩子比根節點小&…

Linux網絡編程服務器模型選擇之并發服務器(上)

轉載&#xff1a;http://www.cnblogs.com/lizhenghn/p/3617666.html 與循環服務器的串行處理不同&#xff0c;并發服務器對服務請求并發處理。循環服務器只能夠一個一個的處理客戶端的請求&#xff0c;顯然效率很低。并發服務器通過建立多個子進程來實現對請求的并發處理。并發…

劍指Offer10- II. 青蛙跳臺階問題

一只青蛙一次可以跳上1級臺階&#xff0c;也可以跳上2級臺階。求該青蛙跳上一個 n 級的臺階總共有多少種跳法。 答案需要取模 1e97&#xff08;1000000007&#xff09;&#xff0c;如計算初始結果為&#xff1a;1000000008&#xff0c;請返回 1。 示例 1&#xff1a; 輸入&a…

Linux網絡編程服務器模型選擇之并發服務器(下)

轉載&#xff1a;http://www.cnblogs.com/lizhenghn/p/3618986.html 前面兩篇文章&#xff08;參見&#xff09;分別介紹了循環服務器和簡單的并發服務器網絡模型&#xff0c;我們已經知道循環服務器模型效率較低&#xff0c;同一時刻只能為一個客戶端提供服務&#xff0c;而且…