⓪編著: 蕭沖
經實測VC6能夠使用最穩定的DirectX版本為 sum2004版,往後的版本因為有加入bufferoverflow的安全性問題函數,使得VC6無法正確編譯。
然而,在使用sum2004版的時候,確也發生了一個致命的問題,在網路上找了許久才在日本的網頁上找到答案… 此問題是
aftcast 發表在 痞客邦 留言(0) 人氣(342)
⓪編著 : 蕭沖
extern "C" 是C++特有的組合關鍵字,在C裡並沒有這個的組合,僅有extern這個關鍵字!
為什麼C++會需要這樣的關鍵字組呢? 原因是C++它有一個複載(overloading)的功能,也就是說同樣的函式名稱可以有多個定義只要參數簽名不同即可。比如說C++裡可以有以下的二個宣告
bar(int i, int j);
aftcast 發表在 痞客邦 留言(3) 人氣(54,722)
⓪著作 :蕭沖
資料的變數是為了方便找資料(memory address 的alias)。
型別則是為了資料「取出後」該用什麼方式處理與呈現。
我們都知道在電腦世界裡,所有資料的最後結構都是010101…二進位。型別則讓我們可以分門別類的把010101的資料做不同的詮釋以呈現我們人類所可以理解的資訊。
Pointer是為了存取資料的方式不同而生,它該是屬於定址問題,也就是說它和變數是該是「同一夥」的!
Type Variable 我的說詞是: 用「直接定址法存取」,資料型別是Type,稱直接變數。
Type *Variable 用「間接定址法存取」,資料型別是Type,稱間接變數。
關於定址法,請參照組合語言(Assembly Language)。
最後被存取的資料才是我們重視的,所以最後被存取的資料型別才是我們關心的,而Pointer雖然也是資料的一部份,但放的都是位址資料,這些位址資料只是為了靈活的操作我們在意的最終資料。
我們在意的資料型別是什麼? 就是 int, char, float, double, stuct, class.....等對人有價的資料。我想,單純的pointer type對我們來說只是存取資料的過程用,並非我們最後想看的吧!
aftcast 發表在 痞客邦 留言(1) 人氣(1,073)
⓪編著 :蕭沖
時間:20060826
許多人都在爭議c/c++的pointer的宣告/定義風格:
type *var1; // code standard 1
type* var2; // code standard 2
似乎第二種方式比較容易讓人了解,但是事實上那樣的風格並不"正確"。
使用第二種風格有一種限制: 不能連續宣告
type* var3, var4 // 只有var3是pointer,var4是一般的變數
就compiler與從assembly的觀點來看,第二種方式才是對的。
每個變數名都代表著memory的位址,變數名只是方便讓我們不用去記憶每個放資料的位址。ASM裡有幾種定址方(addressing):
1/port addressing
2/register addressing //使用register來放資料
3/immediate addressing //直接給常數值
4/memory direct addressing //使用memory的位址放資料
5/memory indirect addressing //使用registers內容所記的位址放資料
4 的方式就像我們宣告一般變數名處理資料一樣
5則像是pointer 或是 array 間接的取資料
在ASM裡,使用[ ]符號來把register包起來,表示資料是放在這個register內資料所記載的位址,而非register的內容。[register]就像是把一般的變數加上一個[ ]的符號來表示它是用間接的方式來取值!就如同C/C++中用*來表達間接取值。
3的方式亦可見於const變數。當變數定義成const時,以後的存取方式都是以直接給值的方式來組譯。我們知道const int 與 int const 是一樣的宣告,如同pointer一樣,個人覺得用int const的方式才是"對"的。而且可以容易了解 int *const ptr的意義就是唯讀指標/針。把const variable翻成立即定址法!
再用個現實例子來說明一下變數與指標:
比如說你有數個倉庫,它們的門牌號碼是100號,103號,108號。100號裡放的是褲子,103號放的是上衣,108號放的是帽子。當我要請工人去拿上衣的時候,我就會說:請去103號拿庫存。經過一陣子,我們的倉庫變更多了,有好多的門牌號,110,111,120,131…我們自己都快記不住哪個號碼是放什麼。於是我們就想了個方法,即在門前貼上貼紙: 把100號貼上一個褲子的圖,把103號貼衣服的圖,把108號貼帽子的圖…當我們想要拿褲子的時候,我們不再告訴工人去100號拿,而是告訴工人去「貼有褲子的倉庫」拿貨。
上面的例子裡,門牌就相當於memory的位址,而貼紙就像是變數名。
那什麼情形是pointer呢? 比如說我們有一批臨時的食品乾貨,當貨來到倉庫時必需因應當時的倉庫使用情形而暫放在某個門牌號,等工人把貨確定放好位子後再回報放在哪個門牌上。為了這種臨時性的需求,我們開了一個門牌號,比如說140號,並也貼了一張辨識用的「食品記錄本圖」,然而裡面不放一般的貨,只放一個記錄本,上面會有工人回報門牌的記錄。於是當我們要工人去拿這臨時貨時,我們會說:去140號查記錄本,然後再去所記錄的門號拿貨來!
這個記錄圖就是代表pointer變數。
一般的圖和「記錄本圖」有什麼不同? 就是拿貨的方式不同。看到一般圖就進倉庫直接拿貨,看到記錄本圖就只道要進去查記錄本,再到確定的地方取貨。從這裡可以了解,貨品本身是哪一類的並不重要,重要的是圖哪一種圖!不同的圖代表不同的取貨方式。因此,你可以想像為何要用
type *var 的宣告。因為var和*var是不同的,var是去var的地位直接拿,*var是到var的地方去查本取貨,僅管這二個的貨品都是同一類的。
這麼說來,你能了解為何我說 type* var是看似合理,但並非根子裡是那樣的。也因此了解為何c/c++中
type* var1, var2 只有var1被compile成pointer。
*號應該看做是變數名的Qualifier(修飾詞),而非type與type結合。
aftcast 發表在 痞客邦 留言(0) 人氣(2,068)
⓪著作 :蕭沖
我常用低階的語言觀點來看高階語言,我覺得那樣可以更深入的了解使用法方,堪至可以模擬出是如何實做的,可能遇到的問題。能想像Bjarne Stroustrup這個創造人當初可能是怎麼想的嗎?
這篇文章最主要的目的是要解釋為何需要用this指標?
前言:想像一下在沒有class之前,C語言程式設計師是如何達成近class模組化功能? 曾看過一些使用C開發的遊戲程式,裡面總定義了一堆的struct,當然還有一堆和這struct有關的function。在cpp裡struct和class是同樣的,都可以定義資料成員(data member)、成員函式(member function/method),僅差別在struct預設成員是public。然而在C語言裡,struct是不能有成員函式定義的,也因此C程式人員可能獨立的把方法寫在Global區塊上,是否可能模擬出把方法也嵌入struct中呢? 這樣就很像class了…請看以下我寫的演示範本:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef
struct StudentInfo
{
char *mName;
int mAgeInAc;
int mAgeInTw;
void (*mpCalc)(struct StudentInfo *self);
int (*mpGetTwAge)(struct StudentInfo *slef);
void (*mCopy)(struct StudentInfo *slef,
struct StudentInfo *src);
}STI;
/* 私人的方法 */
void ac_to_tw(STI *self)
{
self->mAgeInTw = self->mAgeInAc - 1911;
}
/*公開的方法*/
int get_tw_age(STI *self)
{
self->mpCalc(self);
return self->mAgeInTw;
}
/*公開的方法*/
void copy(STI *self, STI *src)
{
*self = *src;
}
/*建構子*/
STI create_sti_object(void)
{
STI object;
object.mName = 0;
object.mpCalc = ac_to_tw;
object.mpGetTwAge = get_tw_age;
object.mCopy = copy;
return object;
}
/*動態建構子*/
STI *p_create_sti_object(void)
{
STI *p_object = (STI *) calloc(1,sizeof(STI));
p_object->mName = 0;
p_object->mpCalc = ac_to_tw;
p_object->mpGetTwAge = get_tw_age;
p_object->mCopy = copy;
return p_object;
}
/*動態解構子*/
void destroy_sti_object(STI *self)
{
free(self);
}
#pragma argsused
int main(int argc, char* argv[])
{
int my_tw_age;
STI student_info;
STI *p_student_info;
STI my_info;
STI babe_info;
STI copy_info;
/*沒有建構式的建立法*/
student_info.mName = "蕭一世";
student_info.mAgeInAc = 1951;
student_info.mpCalc = ac_to_tw;
student_info.mpCalc(&student_info);
printf("%s 民國%d年生\n",student_info.mName,student_info.mAgeInTw);
/*使用動態建構方式,且直接呼叫私人方法*/
p_student_info = p_create_sti_object();
p_student_info->mName = "蕭二世";
p_student_info->mAgeInAc = 1961;
p_student_info->mpCalc(p_student_info);
printf("%s 民國%d年生\n",p_student_info->mName,p_student_info->mAgeInTw);
destroy_sti_object(p_student_info);
/*使用建構子,且呼叫公開方法取回值,Instance1 */
my_info = create_sti_object();
my_info.mName = "蕭沖";
my_info.mAgeInAc = 1971;
my_tw_age = my_info.mpGetTwAge(&my_info);
printf("%s 民國%d年生\n",my_info.mName,my_tw_age);
/*使用建構子,且呼叫公開方法取回值,Instance2A */
babe_info = create_sti_object();
babe_info.mName = "寶貝";
babe_info.mAgeInAc = 1976;
my_tw_age = babe_info.mpGetTwAge(&babe_info);
printf("%s 民國%d年生\n",babe_info.mName,my_tw_age);
/*使用拷備函式,Instance2B */
copy_info = create_sti_object();
copy_info.mCopy(& copy_info, &babe_info);
copy_info.mName = "寶貝拷備";
copy_info.mAgeInAc = copy_info.mAgeInAc + 7;
my_tw_age = copy_info.mpGetTwAge(& copy_info);
printf("%s 民國%d年生\n",copy_info.mName,my_tw_age);
system("pause");
return 0;
}
我們使用了函式指標來實作「方法」的部份。值得注意的部份是:從這裡我們可以看出方法的部份是共享的,僅資料的部份是各自不同(參考最後二個instance)。使用建構式是為了要把方法與Global下的function連結起來,這也是cpp中的一個新觀念-「建構子」。但在這裡還沒有實作出public與private的限制,也就是data hiding的部份。上面的方法中,都有一個參數STI *self這就是為何cpp中的method需要*this的原因了!只是cpp中把這個參數給隱藏起來,在complie時在偷偷的插入這個pointer到最前面的參數,並在method中關於data member和member function的部份加上this->,就如同上面的範例使用了self->。這樣就可以達成「Instance的方法共享,資料私有」。free後method的部份依舊存在。僅data被free了!
範例子寫了一個copy method,演示了cpp中何時你可能會把this這個關鍵字用出來,當實作copy時,你就要自己寫入 *this 這就不是compiler會幫你的了! 還有另一個可能會用到的時候: 傳回物件的reference時return *this,不過這是在reference type 是cpp裡才有的。
aftcast 發表在 痞客邦 留言(1) 人氣(13,884)
⓪編著 :蕭沖
複習一下過去的學習。
當Base Class裡的method與Derive Class中的method形成Overload的情形時,Base中的那些同名method都會被Hide起來。若是試著去使用Base中的那些method時,就會產生error。但可以把instance upcast後使用那些Hide的method,然而這時候Derive中的那些method也會變成無法使用。這個規則無論是Overload一個Base的Virtual或是非Virtual的method都適用!
當Derive Class中Redefine(同名同signature) Base Class中的method時,若此base 的method是Virtual的,那就會產生Override(覆載)。但若base method非Virtual的,那就會產生Hide的情形。
Overload : 同名但不同簽名(signature)
Override : 同名同簽名的Virtual method被再定義
Pure Virtual method不可使用inline方式定義method,但可以使用外部定義的方式。這樣每個子類別就可以方便以Interface::method的方式直接取用。但一般來說是不會在interface上定義method的。
aftcast 發表在 痞客邦 留言(0) 人氣(6,792)
⓪著作 :蕭沖
傳統C語言裡若要初始物件(結構體struct),需使用{ }陣列初始的方法來初始之。但已經不合用於cpp中的class型別,初始化的方式已有所不同。c中使用bitwise copy,一個byte一個byte的copy值。但class中有constructor,並非是簡單的bit copy就可以的。此外,C的其他型別都是用 = 號來初始值。在cpp中擴展了初始化的方法,即用()來做。如: ClassType obj(data); 為了和C的包伏問題,也可以使用 ClassType obj = data來代表同一件事。當然,當建構子的參數超過一個以上你就只能ClassType obj(data1, data2, data3);
cpp裡還有一個功能,允許operator被overloading。於是 = 號則也可能被overloading。造成了某些觀念上會有誤解。比如說:
Foo rhs;
String obj = rhs // statement A
String obj; obj = rhs ; // statments B
A 與 B 這二種寫法是一樣的嗎?
「通常」是一樣的結果,但根子裡是不一樣的。
第二種的寫法compile是先用default constructor先創出一個String物件。然後再使用String裡的 = operator 複載(overload)來做物件copy。值得注意的是當用 = copy時,左右二邊的型別決定了是否可copy。
第一種則單純依overloading 的constuction的型別媒合而create一次!
問題通常會發生在一個物件它的建構子的所有overloading型別與它自己 = operator 的overloading的型別不全然一樣!
實例說明: 以以BCB中Variant這個類別來說
它的reference construction overloading裡有 short*, int*.........
它的 = operator 則是 Variant& __fastcall operator =(const Variant& rhs);
也就是說它接等號右邊可以是 Variant&,說明檔裡更指出如下:
If the Variants can be converted to types that make sense for the given operation, the assignment is performed
好了,實驗一下:
Variant vv =cat->get_ActiveConnection(); // wrong! no such constructor match
Variant vv;
vv = cat->get_ActiveConnection(); //using Variant::operator= overload is match,ok!
結論:
1/ ClassType obj = rhs 與 ClassType obj(rhs) 是等價的! 但用cpp style ( ) 在bcb中會在design time 出現有錯誤,但不用理會。
2/ ClassType objA; objA = rhs ; 這二行就不一定等於上面1說講的一樣。
3/ 我建議使用cpp style 來初始建構,這樣比較不會被 = 號給搞亂了。因為 = 時而是用建構子,時而又是表達二個物件copy。
4/ 是否要分二行陳述則要依情形來看。以剛我講的例子就需要二行。若是一般情形則用一行就可以。而我個人又強力的建議使用cpp的( ) 建構型態!
5/ 一行的效能一定比二行好,所以不要全都使用二行的方式!!
aftcast 發表在 痞客邦 留言(1) 人氣(1,248)