筆記:深度探索C++物件模型 第四章

Function
語意學 ( The Semantics of Function )

摘要:

nonstatic member function

virtual member function

static member function

virtual member function(單一繼承)

多重繼承

函式的效能

Point-to-member functions

inline function

 

nonstatic member function

nonstatic member function 實際上會被內化為 nonmember 的形式,步驟如下:

  1. 改寫函式的 signature 以安插一個額外的參數,用以提供一個存取管道,使 class object 得以將此函式喚起.額外參數就是 this.
  2. 將對 "nonstatic data member 的存取動作" 改經由 this 指標存取.
  3. 改寫成一個外部函式,將函式名稱經過 "mangling" 處理,以成為一個獨一無二的語彙.

名稱的特殊處理 (Name mangling)

一般而言, member 的名稱後面會被加上 class 名稱. 若你宣告 extern "C", 就會壓抑 nonmember
functions 的 "mangling" 效果. 此 mangling 的動作, 各家 compiler 實作方式不同.

 

virtual member functions

若 ptr->normalize(); 

則會被轉化為

(*ptr->vptr[1])(ptr);

其中:

  • vptr 表示由 compiler 產生的指標,指向 virtual table, 其名稱也會被 "mangled", 因為在一個複雜的衍生體系中,可能存在多個
    vptrs.
  • 1 是 virtual tabe slot 的索引值.
  • 第二個 ptr 表示 this 指標.

若宣告為 inline, 則會被 compiler 當作一般 nonstatic member function 一樣地決議,提供極大的效率利益.

 

static member function

一般建議把 static data member 宣告為 nonpublic, 並提供一個或多個 member functions 來存取之.

主要特性: 沒有 this 指標.

次要特性: 他不能直接存取其 class 中的 nonstatic members; 他不能被宣告為 const, volatile, 或 virtual;
他不需要經由 class object 才被喚起.

如果取一個 static member function 的位址, 將獲得其在記憶體中的位置,其位址的型別並不是一個 "指向 class member
function 的指標", 而是一個 "nonmember 函式指標".即

&Point3D::object_count();

會得到

 unsigned int (*)();

而非

 unsigned int (Point3D:*)();

差不多等同於 nonmember function.

p.s. object_count 原型宣告為

unsigned int
Point3D::
object_count() {
return _object_count;
}

 

virtual member function(單一繼承)

單一繼承一般是在每個多型的 class object 身上增加 2 個 member:

  1. 一個字串或數字,表示 class 型別.
  2. 一個指標指向某表格,表格中持有程式的 virtual functions 的執行時期位址.為了找到函式位址,每個 virtual function
    被指派一個表格索引值.

這些工作都由 compiler 完成. 執行時期要做的只是在特定的 virtual table slot 中啟動 virtual function.

圖解. 若 Point3D 繼承 Point2D 繼承 Point, 那麼個別的 virtual table 就可能是

於是當

Point *ptr;
ptr=new Point3D();
ptr->z();

compiler 可以把該呼叫轉化為

(*ptr->vptr[4])(ptr);

 

多重繼承

在多重繼承中支援 virtual function,其複雜度圍繞在第二個及後繼的 base class 上,以及"必須在執行時期調整 this
指標"上.

即後繼的 class 會有多個 virtual table.

將後繼的物件位址指定給一個 base1 指標或 base2 指標時, virtual table 就要視指標的型態作切換,以免呼叫到錯誤的函數.

效率若依照原始 c++ 模型,會變的不好,但這方面各家 compiler 會利用 thunk 或 address points 策略來改善.

虛擬繼承下的 virtual functions

實作上,同樣要調整 this 指標,很複雜,效率也不一定較好,建議不要在一個 virtual base class 中宣告 nonstatic data
members.

 

函式的效能

inline > (nonmember friend=static member=nonstatic member) > virtual
member > virtual member(多重繼承) > virtual member(虛擬繼承)

virtual member 在層數越多的狀況下,其執行時間也成正比增加.

 

Point-to-Member functions

double (Point::*pmf)();
double (Point::*coord)()=&Point::x; //初始
coord=&Point::y; //或是這樣初始

於是可以

(origin.*coord)();

(ptr->*coord)();

這樣用.

實際上則會轉化為

(coord)(&origin);

(coord)(ptr);

支援"指向 virtual member functions"的指標

考慮如下片段(假設 z 為 virtual function)

float (Point::*pmf)()=&Point::z;
Point *ptr=new Point3D;
ptr->z(); //ok
(ptr->*pmf)(); //仍然 ok

compiler 實作上,必須定義 pmf, 使他能持有兩種數值,並且其數值能區分其意義.

多重繼承的狀況:

stroustrup 利用 union 來處理

struct __mptr {
int delta;
int index; //處理 virtual table 索引,不指時為 -1
union {
ptrfunc faddr;
int v_offset; //處理 nonvirtual member function
};
};

於是

(ptr->*pmf)();

會變成

(pmf.index<0)?
(*pmf.faddr)(ptr):
(*ptr->vptr[pmf.index](ptr));

Microsoft 以 vcall thunk 來作檢查,避免浪費檢查的時間,但副作用是,當傳遞一個不變值的指標給 member function 時,需要產生暫時性的物件.

效率:同樣地,不牽涉到"虛擬"+"多重"情況的,效率較佳.

 

inline function

一般處理時,有兩個階段:

  1. 分析函式,若因某些問題(複雜度過高,建構問題…等)被判斷不可 inline, 則會轉為 static 函式,並在被編譯模組中產生對應的函式定義.
  2. 真正的 inline function 擴展動作,是在呼叫的那一點上,這會帶來參數的求值動作及暫時性物件的管理.

通常需進入 assembler 中,才能得知是否真實現了 inline

形式參數擴展的情況大致如下:

inline int min(int i, int j) {
return i<j?i:j;
}
int
main() {
int minval;
int val1=1024,val2=2048;
minval=min(val1,val2);		//minval=val1<val2?val1:val2;
minval=min(1024,2048);		//minval=1024;
minval=min(foo(),bar()+1);	//int t1,t2;
//minval=(t1=foo()),(t2=bar()+1),t1<t2?t1:t2;

 

區域變數的情況

inline int
min(int i, int j) {
int minval=i<j?i:j;
return minval;
}
int local_var;
int minval;
...
minval=min(val1,val2);

則可能會代換為

int local_var;
int minval;
int __min_lv_minval;
minval=(__min_lv_minval=val1<val2?val1:val2),__min_ln_minval;

inline 函式中的區域變數再加上有副作用的參數,可能會導致大量暫時性物件的產生.並且使得程式大小暴增.避免過於複雜的 inline 函式,以免 compiler
無法擴展開來.

筆記:深度探索C++物件模型 第三章

The Semantics of Data

摘要

 

static data member

static data member 永遠在 global data segment, 不影響 class object 的大小.

 

物件的大小

class object 的大小有可能因為 compiler 實作物件模型的方式不同而有不同,原因:

  1. compiler自動加上額外 data member, 以支援某些語言特性.
  2. alignment 邊界調整需要(比如: char 為配合機器特性而以 long 存放)

雖然目前 C++ compiler 已經非常進步,但早期會有兩種程式寫作防禦方格,以防止資料繫結錯誤.

  1. 把 data member 寫在 class 的開頭.
  2. inline function 移到 class 宣告之後,而不在宣告區裡面實作, 如:
extern int x;
class Point3D {
private:
float x,y,z;
public:
float X() const {return x;} 	//到底傳回哪個 x 呢?
float getX() const; 			//早期若不這樣寫,會造成資料繫結到上面那個 x
};
inline float
Point3D::
getX() const {
return x;
}

 

data member 的佈局

data member 實際的存放(佈局)

注意:各 data member 再實際存放時不一定連續.

如:

class Point3D {
float x;
float y;
float z;
};

x,y,z 不一定是連續的,有可能為了要補 alignment 或因compiler 的調整為 y,x,z, 主要原因是 C++ Standard 對此採放任態度.一般而言,仍是連續的.

 

data member 的存取

當 member 被宣告為 static 時,實際上和一般變數存取一樣,因為 static member 存放在 class 之外,不需要再透過 class
去存取. 若不是宣告為 static, 事實上,都會透過一個隱含的 class object (this) 完成,也就是類似這樣

Point3D
Point3D:translate( Point3D* this, const Point3D &pt) {
this->x+=pt.x;
this->y+=pt.y;
this->z+=pt.z;
}

若在程式中對 data member 做存取,如:

origin.y=0.0;

那麼實際上將等於

&origin+(&Point3D::y-1)

指向 data member 的指標,其 offset 值總是被加上 1, 這樣子 compiler就能需分出"一個指向 data member
的指標,用以指出 class 的第一個 member"和"一個指向 data member 的指標,未指向任何 member"的情況.(那就是上面為什麼要
-1的原因).

 

繼承與 data member

在沒有 virtual function 的狀況下,和 struct 相同.原本是獨立不相干的 class 湊成 type / subtype, 並有繼承關係(如
Point2D -> Point3D), 經驗不足的人可能會重複設計一些相同動作的函式,以 constructor 和 operator += 為例,
可以做成 inline. 另外把一個 class 分解為二層或更多層,有可能會為了"表現 class 體系之抽象化"而膨脹所需空間.如:
parent 為兩個 int, child 為一個 char, grandchild 為一個 char, compiler 會為了 alignment
而填補空間.

加上 virtual function 後,將需要導入 vptr, 並在每個函式做 vptr 的處理, 解構時也要把 vptr 抹消,一般而言, vptr
放在 class 的最後面,但 visual c++ 放在最前面,主要是為了繼承的效率問題.

class 若內含一個或多個 virtual base class subobjects, 將會分割為二部分:一個不變區域和一個共享區域,不變區域的資料,不管後繼如何衍化,總擁有固定的
offset(自 object 的起頭算起), 所以可以直接存取,至於共享區域,表現的是 virtual base class subobject, 其位置會因為每次衍生動作而有變化,只能被間接存取,
compiler 會在子類別中安插一些指標,每個指標指向一個 virtual base class.這樣的做法有兩個缺點:

  1. 每個物件必須針對其每個 virtual base class 背負一個額外指標(空間增加).
  2. 由於虛擬繼承串鏈的加長,導致間接存取層次增加.(時間增加)

大部分編譯器到今天仍使用"經由拷貝動作取得所有的 nested virtual base class 指標,放到 derived class
object 之中"來解決第二個問題.

第一個問題一般有兩個解法. Microsoft compiler 引入 virtual base class table, 每一個 class object
如果有一個或多個 virtual base class, 就會由 compiler 安插一個 pointer, 指向 virtual base class
table. 第二個方法是在 virtual function table 中放置 virtual base class 的 offset.

 

指向 data members 的指標

可用以決定 vptr 放在 class 的 begin 或 end, 另一個用途可用來決定 class 中的 access sections.

usage:

	printf("&Point3d::x=%p\n",&Point3d::x);

如果 vptr 在物件尾巴,則 offset 為 0

如果 vptr 在物件起頭,則 offset 為 4

但為何在尾巴時,傳回值總是多 1? 意即 1 主要是用以區分"沒有指向任何 data member" 的指標和 "指向第一個
data member" 的指標.

注意,此種額外的間接性會降低"把所有處理都搬移到暫存器中執行"最佳能力.

筆記:深度探索C++物件模型 第二章

摘要

 

簡介

編譯器可能會有隱含的動作,如對 overload operator 的誤判. 因為 compiler 會自動去尋找最符合其意義的來解釋你的意圖.

 

constructor

以下是各種 default constructor 的建構情形. 一般若 class 沒有宣告 constructor, 那麼 compiler 會在需要時為他建立一個簡單的
constructor, 有四種狀況:

1. class 沒有 constructor, 但成員之中有 member object, 而該 member object 有 default constructor
時, compiler 必定會為前者合成一個 default constructor.

假設

class Dopey {...};
class Sneezy {...};
class Bashful {...};
class Snow_White {
public:
Dopey dopey;
Sneezy sneezy;
Bashful bashful;
private:
int mumble;
};

Snow_White 沒有 default constructor, 那麼 compiler 會為他合成一個 default constructor,
依序喚起 Dopey, Sneezy, Bashful 的 default constructor. 若 Snow_White 定義了 constructor
如下:

Snow_White::Snow_White:sneezy(1024) {
mumble=2048;
}

則 compiler 會擴充為

Snow_White::Snow_White();sneezy(1024) {
dopey.Dopey::Dopey();
sneezy.Sneezy::Sneezy(1024);
bashful.Bashful::Bashful();
mumble=2048;
}

2. 若 class 繼承一個帶有 default constructor 的 class, 則 compiler 會為這個 class 合成一個 constructor,
這個 constructor 只呼叫 parent class 的 constructor.

又若有多個 constructor, 但卻沒有 default constructor 時, compiler 會擴張各 constructor, 以便去呼叫必要的
default constructor. 但卻不會去合成一個新的 default constructor.

3. 若 class 有 virtual function 的時候, compiler 會在 default constructor 中把 vtable
初始化,以便讓子類別能正確呼叫到 virtual function 所對應的 function.

4. virtual base class, 各 compiler 的實作方法不同,但共通點都是要使 virtual base class 在每個 derived
class object 中的位置,能夠在 runtime 時準備妥當.例如:

class X { public: int i; };
class A: public virtual X { public: int j; };
class B: public virtual X { public: double d; };
class C:public virtual X {public: int k;};
//無法在編譯時決議(resolve)出 pa->X::i
void foo( const A* pa) { pa->i=1024; }
int
main() {
foo(new A);
foo(new C);
//...
}

所以, compiler 必須改變"執行存取動作"的程式碼,使得 X::i 能延遲到 runtime 時才決定下來.

以上四種情況都會使 compiler 必須為未宣告 constructor 的 class 合成 default constructor, 這些被 compiler
合成出來的東西, 在 C++ Standard 中稱為 implict nontrival default constructor.

除了這四種情況之外, compiler 不會合成任何 constructor.

另外在合成的 default constructor 中, 只有 base class object 和 member class object 會被初始化,
其他 nonstatic data member,如: int, int*, int array 等都不會.

新手誤解:

1.任何 class 若沒有 default constructor, 就會自動合成一個.

2. compiler合成的 default constructor 會明白設定"class內每個 data member 的預設值".

 

copy constructor

有三種情況會以一 object 內容作為另一 class object 的初值.

  1. string bb; string aa=bb;
  2. object 被當作參數交給函式時.
  3. 函數傳回值是 object 時.

當沒有提供 copy constructor 時,會直接把 member 一個個地複製到要複製的 object 上, 如:

class string {
char *str;
int len;
};

string noun("book");
string verb=noun;

實際上是

verb.str=noun.str;
verb.len=noun.len;

此處的 copy constructor 不等於 copy assignment (operator=)!!

default constructor 和 copy constructor 在必要的時候才用 compiler 產生出來.

請注意 member 若有 pointer 時,那麼預設會把指標指過去,就會有潛在的指標問題!

此種情況應宣告 explicit copy constructor 來解決此問題.如:

class string {
public:
string(const char *);
string(const string&);
};

一般 copy 會有四種情況, class 不展現出 "bitwise copy semantics"(即上述狀況):

  1. 1.class 內含一個 member object 而後者有 copy constructor 時.
  2. 2.class衍生自一個 base class, 而 base class 有 copy constructor 時.
  3. 3.class宣告一個或多個 virtual functions.
  4. 4.當 class 衍生自一個繼承串鏈,其中有一個或多個 virtual base classes.

第三種情況需考慮到之前第一章所提到的 virtual table.

因此若父類別有 animate() 和 draw()這兩個 virtual function,而子類別增加 dance(), 則不能

childclass cc1;
parentclass parent1=cc1;

會造成 virtual table 被切掉,因為 parent1 的 virtual table 根本就沒有 dance.若 parent1宣告為參考或指標時,被
compiler 合成出來的 copy constructor 會把隱含的 vptr 指向 childclass 的 virtual table, 而非
bitwise copy.這個也叫做 upcasting.

第四種情況則請回想一下 virtual base classes, 因為只會有一個實體存在,因此,compiler 會特別審慎考量,不會 bitwise
copy.

NRV最佳化,如下情況會被 compiler

x bar() {
X xx;
//....
return xx;
}

轉為如下的情況

void bar(X& __result) {
__result.X::X();
//....
return;
}

 

NRV

NRV -> Named Return Value 提供重要效率改善.

 

使用 member initialization list.

時機:

  1. 當初始化一個 reference member
  2. 當初始化一個 const member 時
  3. 當喚起一個 base class 的 constructor, 而他擁有一個參數時.
  4. 當喚起一個 member class 的 constructor, 而他擁有一個參數時.

Why:

一般這樣寫, compiler 會先產生一暫存物件, 再 assign 給 member object, 所以效率不彰.

class Word {
String _name;
int _cnt;
public:
Word(){
_name=0;
_cnt=0;
}
};

所以要用 Word::Word:_name(0) {_cnt=0;}; 效率較佳.

缺點及注意事項:

注意 initialization list 的初始順序. 若 member object 依賴性太高,最好不要放到 initialization list
中,而應移至 constructor 中.

What is an Interface

What is an iterface?(什麼是介面)?
在英文裡面,一個介面(Interface)是一組用來與不相干實體互動的設備或系統.根據這個定義,一個遙控器是一組介於你和電視的介面,英文是一組介於兩個人之間的介面,在軍隊中的行為協定是一組在不同階級的人之間的介面.在Java語言中,一組介面是一個與不相干物件互動的設備.一組介面大致可以比擬為一個協定(在行為上的允許).實際上,其他物件導向語言也有介面的功能,但他們稱他們的介面為協定.
腳踏車類別和他的後繼類別定義了一部腳踏車在騎的時候可以做什麼和不可以做什麼,但不包括腳踏車在其他期間與世界的互動.舉例來說,在店裡的一部腳踏車可以被一個庫存程式來管理.一個庫存程式不管物品是什麼類別的,他只要每個物品提供確切的資訊,像是價錢和追蹤號碼,就能管理.取代在其他不相關聯的物品上作強制類別關聯的做法是,庫存程式初始化了一個通訊的協定.這個協定引入常數和函式的集合,包含在一個介面之中.庫存介面將會被定義,但不是實作,裡面的方法將會被用來設定和取得零售價錢,給予一個追蹤號碼等等.
在庫存程式裡作業時,腳踏車類別必須藉著實作這個介面以同意這個協定.當一個類別實作一個介面,這個類別就需要實作在介面中定義的所有方法.例如,腳踏車類別將提供設定和取得零售價格,給予一個追蹤號碼等方法的實作.
使用一個介面去定義一個行為的協定,以用來被任何類別來實作.介面在以下情況是非常有用的:
.在沒有關聯的類別中找到相似點,而不需要強制給予類別的關係.
.定義一個或多個類別預計要實作的方法.
.不需要顯露出物件的類別,就能顯露他的程式化介面.

Windows Script Component 與 NT/2000 的安全

摘要:說明 Windows Script Component 與 NT/2000 檔案安全的關係

內容:

說明
參考

下載本文章的範例程式(下載後以滑鼠右鍵點選,然後選擇註冊即可)

說明

前二日,我在使用WSC(Windows Script Component)的時候,發現了很詭異的事情.

這個元件不管是利用VB或是利用Windows Script Host呼叫,都是正常而且可以運作的.

但是在 ASP 中,卻始終無法使用, 我為了這個問題,搞了兩個多小時,才終於搞懂,這個檔案的安全權限必須要加入IUSR_XXXX(IIS訪客)這個使用者才行!

唉唉唉~~

真是難搞的要命,如果各位有遇到難解的問題,不妨檢查一下資料夾以及檔案的安全權限,也許就能迎刃而解了.

呼~~

上面有提到 WSC, 我在這裡也順便說明一下, WSC 是 Microsoft 提出的一個與 Script 相關的技術,它讓我們可以利用 Script
來撰寫COM的元件,你可以參考http://www.microsoft.com/taiwan/products/develop/scripting/,這裡有許多與
Script 相關的技術文件.

本篇文章所附的範例則是來自 Active Server Pages 3.0 深度探索一書中的範例,主要的用處是結合Recordset來產生一個表格,使用方法很簡單:

dim objtbl
set objtbl = server.createobject("asptable.wsc")
objtbl.addcolumn "員工編號","emp_no",""
objtbl.addcolumn "員工姓名","emp_name", ""
obj.gettext()

不過我改寫過了,增加了兩個方法,一個方法是getpagetext(pageno),可以得到第幾頁的Table;另一個則是obj.getstrtext(),取得整個table的html字串.

當然這裡寫的還不是清楚,以後再來補吧 ^_^

參考

Active Server Pages 3.0 深度探索(國外出版社Wrox,國內則是由碁峰翻譯後發行)

2001/8/30與saint閒聊

Saint said:
Dear, What’s your best program framework ?
—-
Ellery said:
I have no framework in VFP~
大多只是一些小技巧以及函數罷了
你這樣問,有點不知道該怎麼回答你
你想要知道什麼呢??
—-
Saint said:
不一定是VFP
我是說,你沒有想過,有關於framework這樣的事嗎?
從我把資料給物件化時,我就很想要做一個可以自動產生程式的一個framework
到了這裏,又做了menu object 之 後,這一個想法,一直在心中盤算!
所以我想要做一個是my style object framework ,You know ?
可是當我要去做時,發現並不簡單!所以想要聽聽看你的看法!
—-
Ellery said:
有想過~
但真的不簡單
1.需要對該語言的特性作深入的了解
2.需要對物件導向的各種 pattern 有相當的認識,才能作出簡潔而又漂亮的設計
其實也不一定要有相當的認識,但也要有看過大量的Framework
另外,人都會有野心,會想要弄出跨平台,甚至跨語言的一個東西
這樣子的領域就又更龐大了
當我越想越多的時候,就越覺得龐大,越覺得不能馬上動手
所以一直沒有作
跨平台的東西, Borland Kylix and Delphi,Qt 和 Java 是著名的代表
Borland 的產品 CLX
Qt 是 linux 上著名的桌面管理 KDE 的底層,以C++撰寫,但寫出的東西,一樣能在Windows上compile
Java 則更誇張,compile 一次之後, run everywhere,只要檔案複製過去,然後執行環境的Java Virutal Machine有支援,就一切ok
跨語言的東西,COM, CORBA, 以及 beta版的 .Net 應該算是代表
COM, 衍生出 DCOM, COM+
CORBA, 則是 Unix 以及 Unix like 環境下,類似COM的東西,但其實CORBA出來的較早
這個標準實作的比較漂亮且完整,只是支援的語言不多,目前我知道的只有 Java, C++, Object Pascal 而已, Windows 平台上較不常見
.Net 是微軟的一個弘願,Linux上也有了相關的計劃,將來是可以期望的一個東西
說了那麼多,我的最後結論其實是
Framework仍然要作,但是不要像我一樣考慮那麼多,考慮的太多,反而讓自己無法前進
應該依照目前想實作的狀況,一族一族的去實作出來
比如說,現在要作 menu, 就先把 menu 類別及其衍伸類別實作出來
要作檔案,就把檔案類別及其衍生類別實作出來
一個一個去實作
將來如果需要合作的時候,再去參考 Design Pattern 中與合作相關的Pattern去實作
不行,再重寫
但是什麼都要自己寫嗎??
不, 如果現成語言裡已經提供了現成的一個類別, 那麼就直接使用
有跨平台或跨語言的需要,就寫一個wrapper類別把他包起來,將來若跨平台或語言的時候,只要重新實作這個wrapper就可以了
喔~~
回頭看了一下,我居然打了這麼多字
謝謝你的收看
^_^
—-
Saint said:
我倒是沒有想到過說要跨平台這一個問題!
不過我的想法,也倒如你所說的一樣!想的越多,我越難下手去做,
不是沒有想過說,先做一個起來,以後再用合併的方式來做
可是是否會有一個問題,就是所以的物件的起源,我是說如果這樣做的話
我會有一個base object 那有可能我所有的物件都會去參考這一個當做base
想著想著,有時想到如果程式要做,如果只是自己才看的懂那又矢去了意義!
想的越多越難做!
—-
Ellery said:
單一繼承的關係比較簡單,而且效率會較好
八月的時候,我看了侯捷翻譯的”深度探索C++物件模型”
雖然只看到第五章,就因為借書期限到的關係,只好還書
不過有作筆記(這是我第一次看書作筆記!)
對於物件導向實際上的運作了解很多
所以,我想還是建立一個 Base Object
但盡量只賦予必要的特性就可以了
也就是雖然此族的衍生類別都從一個父類別開始繼承,但此父類別必定繼承 Base Object
若真需要增加屬性或方法,只更動此父類別就可以,而不更動父類別
其實對於VFP來說,此動作並不是必要
因為VFP並不是強型別的語言也沒有支援虛擬
所以就不能這樣寫(以下是C++語法)
BaseObject *obj; //宣告成指標
Menu mymenu; //產生一個 mymenu物件,假設 menu繼承 BaseObject
Form myform; //產生一個 myform 物件, 假設 form 繼承 BaseObject
obj=&mymenu; //取得mymenu的位址,指定給obj
obj->activate(); //就可以直接呼叫 menu 的方法
obj=&myform; //取得myform的位址,指定給obj
obj->show(); //就可以直接呼叫 form 的方法
很神奇不是嗎?? 如果你用過強型別的語言
對這種神奇的感覺會很強烈
因為類別不同是不可以隨便指定的!換言之也不能隨意呼叫其他類別的方法
Java, Object Pascal 也都有類似的用法
VFP和VB 所有的變數都是 Variant, 這樣的感覺就不是很強烈
反正怎麼寫怎麼適用
local obj
obj=createobject(“menu”)
obj.activate()
obj=createobject(“form”)
obj.show()
但我就不清楚VFP內部是怎麼去實作的
說到程式看不看的懂
又牽扯到寫作風格的問題
我以前很痛恨這種寫作風格的問題
尤其是學校導師(後來老師因為肝的問題,英年早逝了,很遺憾沒去醫院看他最後一面)教我們COBOL的時候, 他一直不厭其煩的要求我們要這樣子去寫 COBOL 程式
那時候覺得如果強制要求寫作的風格,會很麻煩,而且排版的很累,寫出來的程式碼也會不簡潔,不漂亮
但是後來看過”撰寫0錯誤程式”這本書以及經過歷練之後就不會這樣想了
現在會盡量要求自己留下註解, 使用較清楚的方法去寫程式
假設前兩天寫出這樣的程式
if getanswer()
*do something
else
*do something
end if
隔兩天,我一定忘記我在寫什麼,於是又要去看 getanswer() 在寫什麼,浪費了許多時間
我會盡量這樣寫
*利用 getanswer() 函數詢問使用者的答案, true 表示使用者回答 yes
if getanswer()==true
*do something
else
*do something
end if
這樣至少我自己看程式會知道我之前要幹麼
哇~又打了這麼多
8-p

scriptlet的問題

我解決了這個怪問題~~
由於scriplet元件是由 scrobj.dll 去負責解譯
因此我把我這台電腦(ie5.5)的 scrobj.dll 複製到對方的電腦(ie5.0)中
於是就不會發生此問題了~~
參考http://support.microsoft.com/support/kb/articles/Q247/7/84.ASP
的時候就有想到可能是版本的原因
更新到 ie 5.5 後應該可以解決
但是對方的電腦由於 Ultimus Flow 系統的原因,所以無法更新到 5.5
於是只好嘗試更新單獨的 dll, 一舉成功!!
哈哈哈哈

搜尋CORBA products

除了 inprise 的 visibroker 之外
有許多的corba 產品
http://www.omg.org/technology/corba/corbadownloads.htm
之中,有介紹許多.
以下是我覺得不錯的
http://www.ooc.com 提供免費的corba及其相關產品可供download,適用於各種平台,主要開發語言是 C++ and Java, 產品都實作了omg的標準; 另外也有提供嵌入式系統用的corba
下載網頁; 還有就是須要配合特別的JThreads/C++,是一個轉換C++與Java的東西!!
http://www.ooc.com/ob/download4.html
http://www.ooc.com/obe/download.html
OmniORB 也是一個不措的
omniORB is a robust, high performance CORBA 2.3 compliant ORB with C++ and Python bindings. Features include:
openorb 則是一個 open source 的網站,只是目前還沒有一個標準的package出現
另外還有,暫時先逛到這裡