宣告一個新的 c++ 類型時, 如果在類型裏面使用動態記憶空間分配(可以使用 new 運算子或是 malloc( ) 函式傳回指標加以運用),就必須知道 6 種類型的方法及使用時機, 避免造成重複刪除的窘境. 類型裡面的成員分成可記憶的變數與不具記憶的函數(函式的傳回值), 這種在類型裡面的變數稱為狀態(state),而函式則特別稱為方法(method),當類型被引用要產生新物件時第一個被執行的就是類型建構法, 此時裏面變數的狀態都是混沌未明, 透過建構法才能將它初始化,當建構法完成,就會回傳一個該類型的新物件(裏面包含了已初始過的狀態與方法), 因此建構法最後無需再用 return 之類的程式碼. 當物件以指標型式傳進來時就要用箭頭符號引用成員, 如果是物件型式就用句號引用成員, new 回傳的的是物件指標, 使用 new 所分配的記憶空間必須用 delete 將它釋放. 在方法中使用的 this 就是該物件本身的指標. 指標的存取是一項危險的動作, 一旦指標成為空指標時(null pointer), 不小心產生 Null Pointer Exception(俗稱 NPE), 嚴重的話造成當機.
1. 解構法(destructor), 函式的特徵是 "~類型名稱 ( )", 物件消滅前, 當成員曾經 用new 或 malloc( )分配動態記憶空間時, 要在此要用程式碼 delete 運算子或函式 free( )將它釋放出來.
~className ( ) { // Here free all of dynamic resouce
// delete dynamicResource;
}
2. 基本建構法(constructor),無傳遞參數(void), 單純初始化內部狀態用,初始類型的陣列也會用此法來建構新物件
className ( ) { // Here init all states ...
// dynamicResource =NULL;
} // No need to use return in the end. Do automatically by the compiler
3. 複本建構法(copy constructor), 此函式的特徵是"類型名稱 (物件)", 顧名思義就是要從一份物件來源去產生新類型的物件.例如:
className (className& source) { // Here init and copy all state from source
// dynamicResource = new className( );
} // No need to use return in the end. Do automatically by compiler
4. 物件指定運算(copy assignment), 發動的時機是一個有 = 類型的運算式, 將產生的物件指定給一個變數. 因為運算函式並非建構法, 因此運算函式最後要用 retrun *this 或是 return this; 視傳回的型態(type)而結束. 物件指定運算的特徵是 "類型名稱 operator = (物件)", 在運算函式裏面傳進來的參數必須先判斷非本尊(this) 時, 才需要作指定運算:
className& operator = (classNamme &source) {
if( &source != this ) { // this is a pointer, &source is source pointer
// Here do copy assignment ...
// dynamicResource = source.dynamicResource; // share dynamicResource
}
return *this; // object itself
}
5. 物件轉移運算(move assignment), 顧名思義就是一個將資產(resource)換手(transfer)的過程,把物件轉交給一個既有物件. 其特徵是 "類型& operator =
(類型&& )",其發動的時機也是在一個有 = 類型的運算式, 通常是由編譯器視需求, 先產生一個暫時類型的物件(稱為匿名物件, 也許會透過 copy assignment 產生匿名物件)傳進來, 而受者本身的成員若之前使用 new 分配過動態記憶空間, 就要在此時先釋放,之後將它指向匿名物件的資源,避免記憶體洩漏(memory leak)的情況, 甚至當匿名物件的成員也帶有動態記憶空間時, 也必須最後將該成員設定為 NULL, 當交易完成,匿名物件也會跟著消滅, 這樣才不致在解構時發生重複釋放(double free)記憶體的危機, 當然轉移之前也要先判定來源,避免自我轉移的窘境:
className& operator = (classNamme&& temp) { // temp is generated by compiler
if( &temp != this ) { // this is a pointer, &temp is source pointer
// Here do move assignment ...
// if( dynamicResource != NULL ) delete dynamicResource;// free resource
// dynamicResource = temp.dynamicResource; // link to resource
// temp.dynamicResource = NULL; // temp cut off the resource
}
return *this; // object itself
} // temp is destroy automatically by compiler
6. 交易建構法(move constructor), 函式的特徵是 "類型 (類型&& )", 同樣是將資產轉手用的, 只不過是由程式碼主動呼叫(move semantics, 可以透過 std::move(object) 來發動),他是建構法類型其中之一, 編譯器直接將物件轉化成匿名物件交由該建構法運用, 最後另外產生該類型的新物件, 要注意的是物件經轉移後, 其成員狀態, 如果有分配動態記憶空間時, 是否將成員指定為 NULL,視交易建構法程式碼而定,當然也須避免發生重複釋放的危機.
classNamme(&& temp) { // temp is generated by compiler
// Here do move assignment ...
// dynamicResource = temp.dynamicResource; // link to dynamicResource
// temp.dynamicResource = NULL; // Cut off the link of dynamicResource
} // temp is destroy automatically by compiler,
7. 範例程式碼:
// hex.c
#include "stdio.h"
#include "iostream"
#include "stdarg.h"
struct hex {
const char *str;
int length;
char id;
int numtable(char c) { // λ(c)
if ('0' <= c && c <= '9') return c - '0';
else if ('a' <=c && c <= 'f') return c - 'a' + 10;
else if ('A' <=c && c <= 'F') return c - 'A' + 10;
else return 0;
};
char hextable(int i) { // λ(i)
char ch;
if (0 <= i && i <= 9) ch = i + '0';
else if (10 <= i && i <= 15) ch = i - 10 + 'a';
else ch = '0';
return ch;
};
~hex(void) { // destructor
printf("Destroy ~ ");
if (str) {
printf("%s ! ",str);
delete str;
} else printf("Empty ! ");
printf("finish\n");
}
hex(void) { // 1 init constructor
printf("Init constructor(): \n");
str = NULL;
length=0 ;
id=0;
}
hex(hex &a) { // 2 copy constructor
printf("Copy Constructor(%s): ", a.str);
str = string2hex(a.str); // need a new copy
printf("finish\n");
}
hex(const char *s) { // 2 copy constructor
printf("Copy Constructor(%s)\n",s);
str = string2hex(s);
}
hex(hex &&a) { // 3 move constructor
// printf("Move constructor(%s): ", a.str);
str = a.str; // just steal
length = a.length;
id = a.id;
a.str = NULL;
printf("Move Constructor(%s) finish\n",str);
}
// Assignment is an operator, need a return type, and must not assign by itself
hex& operator =(hex &a) { // 4 copy assignment
printf("Copy assign = %s ", a.str);
if( &a != this) {
str = a.str;// point to the string
length=0; while (a.str[length++]);// caculate the the length
id = 0;
tocstr(); // build a new string
}
printf("finish\n");
return *this; // *this;
}
hex& operator =(char *a){ // 4 copy assignment
printf("String assign = %s ", a);
if( str != a) {
str = a; // point to new string
length=0; // caculate the the length
id = 0;
while (a[length++]);
tocstr(); // conver to string
}
printf("finish\n");
return *this; // *this;
}
hex& operator =(hex &&a){ // 5 move assignment
printf("Move assign = %s, self str = %s\n", a.str, str);
if( &a != this ) { // if pointer of a is not this
if( str ) delete str;
str = a.str;
length = a.length;
id = a.id;
a.str = NULL; // to indicate not free a.str when destroy a
}
printf("Move = finish\n");
return *this;
}
hex& printf(const char *fmt, ...){// to chain printf
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
return *this; // method chain if necessary
}
hex& tohex(){
if( id !=1 && length > 0) {
int k = length ;
auto number = new char[(k << 1) + 1];
int length = 0;
for (int i = 0; i < k; i++){
unsigned char c = str[i];
number[length++] = hextable((c&0xf0) >> 4);//convert to HEX char, MSB
number[length++] = hextable(c&0xf); //convert to HEX char, LSB
}
number[length++]=0; //EOS
}
return *this;
}
hex& tocstr(){
if( id !=2 && length>0 ) {
int k = length;
auto cstring = new char[(k >> 1) + 1];// include EOS, and each HEX number occupy 2 bytes
length = 0;
for (int i = 0; i < k; i += 2) cstring[length++] = (numtable(str[i]) << 4) + numtable(str[i+1]);
cstring[length++]=0;//EOS
str = cstring;
}
return *this;
}
const char *string2hex(const char *sx){
id = 1;
int k =0 ;
while (sx[k]) k ++; // break when EOS
auto number = new char[(k << 1) + 1];// include EOS, and each HEX number occupy 2 bytes
length = 0;
for (int i = 0; i < k; i++){
unsigned char c = sx[i];
number[length++] = hextable((c&0xf0) >> 4);//convert to HEX char, MSB
number[length++] = hextable(c&0xf); //convert to HEX char, LSB
}
number[length++]=0; //EOS
return number;
}
const char *hex2string(const char *number) {
id = 2;
int k = 0 ;
while (number[k]) k ++; // break when EOS
auto cstring = new char[(k >> 1) + 1];// include EOS, and each HEX number occupy 2 bytes
length = 0;
for (int i = 0; i < k; i += 2) cstring[length++] = (numtable(number[i]) << 4) + numtable(number[i+1]);
cstring[length++]=0;//EOS
return cstring;
}
};
int main(){
printf("\n1.\n");
hex aaa;
printf("\n2.\n");
auto sss = hex("123");
auto strhex = hex("中文也可以");
printf("\n3.\n");
sss = strhex.str;
printf("\n4.\n");
auto bbb = std::move(strhex);
printf("\nfinal\n");
}
執行 g++ hex.c && ./a.out 輸出結果:
1.
Init constructor():
2.
Copy Constructor(123)
Copy Constructor(中文也可以)
3.
Copy Constructor(e4b8ade69687e4b99fe58fafe4bba5)
Move assign = 653462386164653639363837653462393966653538666166653462626135, self str = 313233
Move = finish
Destroy ~ Empty ! finish
4.
Move Constructor(e4b8ade69687e4b99fe58fafe4bba5) finish
final
Destroy ~ e4b8ade69687e4b99fe58fafe4bba5 ! finish
Destroy ~ Empty ! finish
Destroy ~ 653462386164653639363837653462393966653538666166653462626135 ! finish
Destroy ~ Empty ! finish
沒有留言:
張貼留言