簡介
類型安全機制的實現(xiàn)原來采用的是C風格的回調(callback)函數,而.NET Framework引入了委托和事件來替代原來的方式;它們被廣泛地使用。我們在這里嘗試使用標準C++來實現(xiàn)與之類似的功能,這樣我們不但可以對這些概念有一個更好的認識,而且同時還能夠體驗C++的一些有趣的技術。
C#中的委托與事件關鍵字
首先我們來看一個簡單的C#程序(下面的代碼略有刪節(jié))。執(zhí)行程序的輸出結果如下顯示:
SimpleDelegateFunction called from Ob1,
string=Event fired!
Event fired!(Ob1): 3:49:46 PM on
Friday, May 10, 2002
Event fired!(Ob1): 1056318417
SimpleDelegateFunction called from Ob2,
string=Event fired!
Event fired!(Ob2): 3:49:46 PM on
Friday, May 10, 2002
Event fired!(Ob2): 1056318417
所有這些都源于這樣一行代碼:dae.FirePrintString("Event fired!");
在利用C++來實現(xiàn)這些功能時,我模仿了C#的語法并完全按照功能的要求進行開發(fā)。
namespace DelegatesAndEvents
{
class DelegatesAndEvents
{
public delegate void PrintString(string s);
public event PrintString MyPrintString;
public void FirePrintString(string s)
{
if (MyPrintString != null)MyPrintString(s);
}
}
class TestDelegatesAndEvents
{
[STAThread]
static void Main(string[] args)
{
DelegatesAndEvents dae =new DelegatesAndEvents();
MyDelegates d = new MyDelegates();
d.Name = "Ob1";
dae.MyPrintString +=new DelegatesAndEvents.PrintString(d.SimpleDelegateFunction);
// ... more code similar to the
// above few lines ...
dae.FirePrintString("Event fired!");
}
}
class MyDelegates
{
// ... "Name" property omitted...
public void SimpleDelegateFunction(string s)
{
Console.WriteLine("SimpleDelegateFunction called from {0}, string={1}", m_name, s);
}
// ... more methods ...
}
}
C++中的類型安全函數指針
對于“老式方法”的批判之一便是它們不是類型安全的[1]。下面的代碼證明了這個觀點:
typedef size_t (*FUNC)(const char*);
void printSize(const char* str) {
FUNC f = strlen;
(void) printf("%s is %ld chars\n", str, f(str));
}
void crashAndBurn(const char* str) {
FUNC f = reinterpret_cast<FUNC>(strcat);
f(str);
}
代碼在[2]中可以找到。當然,在你使用reinterpret_cast的時候,你可能會遇到麻煩。如果你將強制轉換(cast)去掉,C++編譯器將報錯,而相對來說更為安全的static_cast也不能夠完成轉換。這個例子也有點像比較蘋果和橙子,因為在C#中萬事萬物皆對象,而reinterpret_cast就相當于一種解決方式。下面的這個C++程序示例將會采取使用成員函數指針的方法來避免使用reinterpret_cast:
struct Object { };
struct Str : public Object {
size_t Len(const char* str) {
return strlen(str);
}
char* Cat(char* s1, const char* s2) {
return strcat(s1, s2);
}
};
typedef size_t (Object::*FUNC)(const char*);
void printSize(const char* s) {
Str str;
FUNC f = static_cast<FUNC>(&Str::Len);
(void) printf("%s is %ld chars\n", s, (str.*f)(s));
}
void crashAndBurn(const char* s) {
Str str;
FUNC f = static_cast<FUNC>(&Str::Cat);
(str.*f)(s);
}
static_cast運算符將轉化Str::Len函數指針,因為Str是由Object派生來的,但是Str::Cat是類型安全的,它不能被轉換,因為函數簽名是不匹配的。
成員函數指針的工作機制與常規(guī)的函數指針是非常相似的;不同(除了更為復雜的語法外)的是你需要一個用來調用成員函數的類的實例。當然,我們也可以使用->*運算符來用指向類實例的指針完成對成員函數的調用。
Str* pStr = new Str();
FUNC f = static_cast<FUNC>(&Str::Len);
(void) printf("%s is %ld chars\n", s, (str->*f)(s));
delete pStr;
只要所有的類是從基類Object派生來的(C#中就是這樣),你就可以使用C++來創(chuàng)建類型安全的成員函數指針。
類型安全機制的實現(xiàn)原來采用的是C風格的回調(callback)函數,而.NET Framework引入了委托和事件來替代原來的方式;它們被廣泛地使用。我們在這里嘗試使用標準C++來實現(xiàn)與之類似的功能,這樣我們不但可以對這些概念有一個更好的認識,而且同時還能夠體驗C++的一些有趣的技術。
C#中的委托與事件關鍵字
首先我們來看一個簡單的C#程序(下面的代碼略有刪節(jié))。執(zhí)行程序的輸出結果如下顯示:
SimpleDelegateFunction called from Ob1,
string=Event fired!
Event fired!(Ob1): 3:49:46 PM on
Friday, May 10, 2002
Event fired!(Ob1): 1056318417
SimpleDelegateFunction called from Ob2,
string=Event fired!
Event fired!(Ob2): 3:49:46 PM on
Friday, May 10, 2002
Event fired!(Ob2): 1056318417
所有這些都源于這樣一行代碼:dae.FirePrintString("Event fired!");
在利用C++來實現(xiàn)這些功能時,我模仿了C#的語法并完全按照功能的要求進行開發(fā)。
namespace DelegatesAndEvents
{
class DelegatesAndEvents
{
public delegate void PrintString(string s);
public event PrintString MyPrintString;
public void FirePrintString(string s)
{
if (MyPrintString != null)MyPrintString(s);
}
}
class TestDelegatesAndEvents
{
[STAThread]
static void Main(string[] args)
{
DelegatesAndEvents dae =new DelegatesAndEvents();
MyDelegates d = new MyDelegates();
d.Name = "Ob1";
dae.MyPrintString +=new DelegatesAndEvents.PrintString(d.SimpleDelegateFunction);
// ... more code similar to the
// above few lines ...
dae.FirePrintString("Event fired!");
}
}
class MyDelegates
{
// ... "Name" property omitted...
public void SimpleDelegateFunction(string s)
{
Console.WriteLine("SimpleDelegateFunction called from {0}, string={1}", m_name, s);
}
// ... more methods ...
}
}
C++中的類型安全函數指針
對于“老式方法”的批判之一便是它們不是類型安全的[1]。下面的代碼證明了這個觀點:
typedef size_t (*FUNC)(const char*);
void printSize(const char* str) {
FUNC f = strlen;
(void) printf("%s is %ld chars\n", str, f(str));
}
void crashAndBurn(const char* str) {
FUNC f = reinterpret_cast<FUNC>(strcat);
f(str);
}
代碼在[2]中可以找到。當然,在你使用reinterpret_cast的時候,你可能會遇到麻煩。如果你將強制轉換(cast)去掉,C++編譯器將報錯,而相對來說更為安全的static_cast也不能夠完成轉換。這個例子也有點像比較蘋果和橙子,因為在C#中萬事萬物皆對象,而reinterpret_cast就相當于一種解決方式。下面的這個C++程序示例將會采取使用成員函數指針的方法來避免使用reinterpret_cast:
struct Object { };
struct Str : public Object {
size_t Len(const char* str) {
return strlen(str);
}
char* Cat(char* s1, const char* s2) {
return strcat(s1, s2);
}
};
typedef size_t (Object::*FUNC)(const char*);
void printSize(const char* s) {
Str str;
FUNC f = static_cast<FUNC>(&Str::Len);
(void) printf("%s is %ld chars\n", s, (str.*f)(s));
}
void crashAndBurn(const char* s) {
Str str;
FUNC f = static_cast<FUNC>(&Str::Cat);
(str.*f)(s);
}
static_cast運算符將轉化Str::Len函數指針,因為Str是由Object派生來的,但是Str::Cat是類型安全的,它不能被轉換,因為函數簽名是不匹配的。
成員函數指針的工作機制與常規(guī)的函數指針是非常相似的;不同(除了更為復雜的語法外)的是你需要一個用來調用成員函數的類的實例。當然,我們也可以使用->*運算符來用指向類實例的指針完成對成員函數的調用。
Str* pStr = new Str();
FUNC f = static_cast<FUNC>(&Str::Len);
(void) printf("%s is %ld chars\n", s, (str->*f)(s));
delete pStr;
只要所有的類是從基類Object派生來的(C#中就是這樣),你就可以使用C++來創(chuàng)建類型安全的成員函數指針。