編輯:關於Android編程
Android版本:4.4.2
在C++語言中,指針操作是最容易問題的地方,常見的指針操作錯誤有以下幾種:
1、 定義一個指針,但沒有對其進行初始化。這種情況下,指針會指向一個隨機地址,此時使用該指針,將出現不可預知的錯誤。一般定義一個指針時,應該同時對該指針進行初始化。
2、 new了一個對象後,忘記delete該對象。這種情況會造成內存洩漏,時間久了,重復多次,可能造成系統宕機。
3、 野指針。例如,我們new了一個對象A,並用指針p1指向A,使用結束後,我們delete了對象A,此時,p1還是指向A原來的地址,但是A被delete後,該地址是非法地址。這樣的p1就是野指針。再舉一個例子,p1和p2兩個指針都指向A,我們通過p1指針delete了A之後,將p1設置為NULL,但p2仍然指向A原來地址,此時,p2就是野指針。
為了避免上述C++指針使用錯誤,Android為我們提供了智能指針,定義在frameworks/rs/cpp/util目錄下的RefBase.h和StrongPointer.h文件中。
Android智能指針是一個模板類,又分為強指針sp和弱指針wp。強指針sp定義如下:
62template63class sp 64{ 65public: 66 inline sp() : m_ptr(0) { } 67 68 sp(T* other); 69 sp(const sp & other); 70 template sp(U* other); 71 template sp(constsp& other); 72 73 ~sp(); 74 75 // Assignment 76 77 sp& operator = (T* other); 78 sp& operator = (const sp &other); 79 80 template sp& operator= (const sp& other); 81 template sp& operator= (U* other); 82 83 //! Special optimization for use byProcessState (and nobody else). 84 void force_set(T* other); 85 86 // Reset 87 88 void clear(); 89 90 // Accessors 91 92 inline T& operator* ()const { return *m_ptr; } 93 inline T* operator-> () const {return m_ptr; } 94 inline T* get() const { return m_ptr; } 95 96 // Operators 97 98 COMPARE(==) 99 COMPARE(!=) 100 COMPARE(>) 101 COMPARE(<) 102 COMPARE(<=) 103 COMPARE(>=) 104 105private: 106 template friend class sp; 107 template friend class wp; 108 void set_pointer(T* ptr); 109 T* m_ptr; 110};
66-71行,定義了5種sp構造函數。
73行,定義了sp的析構函數。
77-81行,定義了4種“=”運算符的重載函數。
92-103行,對其它8種運算符進行重載。每個COMPARE宏對應該運算符的6個重載函數。
109行,定義T類型指針變量m_ptr。這個指針變量m_prt即是sp類的核心。
我們可以這樣理解sp類:
1、sp類的對象實例用來替代我們原來所用的指針。
2、sp類是對指針的封裝。sp.m_prt即我們原來所用的指針。
3、通過使用sp類的對象代替指針,可以避免出現原來使用指針時常見的錯誤。
為什麼說使用sp類的對象代替指針,就可以避免原來使用指針時常見的錯誤呢?
首先來看使用指針的第一種常見錯誤,即定義指針時沒有進行初始化。
使用sp類對象代替指針後,創建sp類對象時會調用到sp類構造函數,在構造函數中,會對sp.m_ptr進行初始化,例如:
66 inline sp() : m_ptr(0) { }
默認構造函數將sp.m_ptr初始化為0。
再比如:
120template121sp ::sp(T* other) 122: m_ptr(other) 123 { 124 if (other)other->incStrong(this); 125 }
該構造函數將sp.m_ptr初始化為通過參數傳遞進來的other。
除了構造函數,對sp對象進行初始化還可能通過賦值運算符,例如:
sp
這種情況下,就用到了sp的“=”重載運算符:
162template163sp & sp ::operator = (T* other) 164{ 165 if (other) other->incStrong(this); 166 if (m_ptr)m_ptr->decStrong(this); 167 m_ptr = other; 168 return *this; 169}
可以看到,在“=”重載運算符中,167行,將參數傳遞進來的other賦值給sp.m_ptr。
這樣通過在構造函數和重載賦值運算符中完成對sp.m_ptr的初始化,即避免了使用指針的第一種常見錯誤(定義指針時忘記初始化)。
使用指針的第二種常見錯誤(new一個對象後忘記delete)和第三種常見錯誤(野指針)可以通過給被指針指向的對象加一個引用計數器來解決。我們可以想象一下,如果被指針指向的對象有一個引用計數器,即當有一個指針指向該對象時,該對象引用計數器為1,有兩個指針指向該對象時,該對象引用計數器為2,依次類推。反之,當一個指針不再指向該對象時,該對象引用計數器的值減1,當對象引用計數器的值為0時,該對象需要被delete。
怎樣給對象設置一個引用計數器呢?Android智能指針的做法是讓該對象對應的類繼承LightRefBase模板類,該類定義在frameworks/rs/cpp/util/RefBase.h文件中:
163template164class LightRefBase 165{ 166public: 167 inline LightRefBase() :mCount(0) { } 168 inline voidincStrong(__attribute__((unused)) const void* id) const { 169 __sync_fetch_and_add(&mCount, 1); 170 } 171 inline voiddecStrong(__attribute__((unused)) const void* id) const { 172 if(__sync_fetch_and_sub(&mCount, 1) == 1) { 173 deletestatic_cast (this); 174 } 175 } 176 //! DEBUGGING ONLY: Getcurrent strong ref count. 177 inline int32_tgetStrongCount() const { 178 return mCount; 179 } 180 181 typedefLightRefBase basetype; 182 183protected: 184 inline ~LightRefBase() { } 185 186private: 187 friend classReferenceMover; 188 inline static void moveReferences(void*,void const*, size_t, 189 constReferenceConverterBase&) { } 190 191private: 192 mutable volatile int32_tmCount; 193};
192行,定義了一個整數mCount,這就是所謂的引用計數器。
167行,LightRefBase的構造函數將引用計數器mCount初始化為0。
168-170行,定義了incStrong函數,用於將引用計數器mCount的值加1。
171-175行,定義了decStrong函數,用於將引用計數器mCount的值減1,需要注意的是,如果減1之前,mCount的值為1,說明對本對象最後的引用也解除了,則會delete本對象,這樣就避免了我們所說的new一個對象,忘記delete。
知道了LightRefBase的定義,我們再回過頭來看sp類,就能理解智能指針是怎樣工作的了。
sp的構造函數如下:
120template121sp ::sp(T* other) 122: m_ptr(other) 123 { 124 if (other)other->incStrong(this); 125 }
sp的重載賦值運算符如下:
162template163sp & sp ::operator = (T* other) 164{ 165 if (other)other->incStrong(this); 166 if (m_ptr)m_ptr->decStrong(this); 167 m_ptr = other; 168 return *this; 169}
類T繼承LightRefBase,可以看到,通過構造函數或賦值運算讓sp對象指向T對象,除了將other賦值給sp.m_ptr外,因為這兩種情況都屬於增加一個對象引用計數,所以還會調用other->incStrong(this)。特別需要注意的是,在賦值運算中,如果m_ptr之前指向其它值,則需要先調用m_ptr->decStrong(this),即對應對象引用計數減1,然後再將other賦值給sp.mptr。
sp的析構函數如下:
147template148sp ::~sp() 149{ 150 if (m_ptr)m_ptr->decStrong(this); 151}
可見,當智能指針sp析構時,會調用m_ptr->decStrong(this)讓對應對象的引用計數器減1。
這兩天Google更新了Android Studio 1.2正式版,新版本的設置界面大變面,設置條目較舊版本進行了歸類,不像以前那樣列表長長的了。趁著安裝新版本的機會,把
如果不考慮更深層的性能問題,我個人認為ScrollerView還是很好用的。而且單用ScrollerView就可以實現分類型的RecyclerView或ListView所
如何在圖片上畫畫呢?這裡寫了一個demo,供大家參考一、先看一眼工程結構工程結構:二、自定義view這個自定義view實現了保留軌跡的功能,代碼如下package pic
SVG格式, 適應屏幕, 圖片較小, 還有很多優點, 參考. 本文講解如何使用SVG格式. SVG: Scalable Vector Graphics, 可縮放矢量圖形.