(搬運(yùn)工)當(dāng)多態(tài)遇上數(shù)組
Posted on 2012-02-15 16:46 點(diǎn)點(diǎn)滴滴 閱讀(277) 評論(0) 編輯 收藏 引用 所屬分類: 02 編程語言1. 能力測試
請回答下面的問題:
實現(xiàn)多態(tài)的效果,我們需要具備哪些條件?
上面的題目僅用于測試你是否具備閱讀本文的必要條件。如果你對此題毫無頭緒,那么我建議你盡快交叉本文。當(dāng)然,要順暢閱讀本文,你還需要具備一些C++、C++/CLI和C#的基礎(chǔ)知識。我想不必特別聲明,建議當(dāng)然是主觀的。不過我愿意再說一次:建議是主觀的,你有選擇。
2. ... In C++,
好吧,既然你走到這一步,我建議你堅持下去。當(dāng)然,你還是隨時可以交叉本文,總之,你有選擇。來,請先看看如下代碼:
// Code #01
#include
#include 

{
public:
virtual void Print()
{
std::cout << "A" << std::endl;
}
};
class B : public A
{
public:
virtual void Print() override
{
std::cout << "B" << std::endl;
}
};
class C : public A
{
public:
virtual void Print() override
{
std::cout << "C" << std::endl;
}
};
void Print(A arr[], int count)
{
for (int i = 0; i < count; ++i)
{
arr[i].Print();
}
}
int _tmain(int argc, _TCHAR* argv[])
{
const int COUNT = 11;
A a_arr[COUNT];
Print(a_arr, COUNT);
B b_arr[COUNT];
Print(b_arr, COUNT);
C c_arr[COUNT];
Print(c_arr, COUNT);
return 0;
}
- A:請告訴我,Code #01有沒有問題?
- B:Easy job!把它編譯并運(yùn)行一下就知道了唄!
接著,阿B編譯了這段代碼,程序運(yùn)行后,阿B發(fā)現(xiàn)輸出結(jié)果正是我們所期望的。于是,阿B得出結(jié)論:這段代碼沒問題!
然而,你是否知道這段代碼其實隱藏著一個計時炸彈,只要時機(jī)一到,它就會引爆并使你的程序完全癱瘓?不信?那我只好讓你見識一下這個炸彈引爆是的情景了。現(xiàn)在,我分別為class B和class C加入一個field,并修改一下main()以便給class B和class C里的field賦值(注意,本賦值操作不是必須的!):
// Code #02
#include
#include 

{
public:
virtual void Print()
{
std::cout << "A" << std::endl;
}
};
class B : public A
{
public:
virtual void Print() override
{
std::cout << "B" << std::endl;
}
long L;
};
class C : public A
{
public:
virtual void Print() override
{
std::cout << "C" << std::endl;
}
double D;
};
void Print(A arr[], int count)
{
for (int i = 0; i < count; ++i)
{
arr[i].Print();
}
}
int _tmain(int argc, _TCHAR* argv[])
{
const int COUNT = 11;
A a_arr[COUNT];
Print(a_arr, COUNT);
B b_arr[COUNT];
for (int i = 0; i < COUNT; ++i)
{
b_arr[i].L = 141214121412;
}
Print(b_arr, COUNT);
C c_arr[COUNT];
for (int i = 0; i < COUNT; ++i)
{
c_arr[i].D = 3.141592654;
}
Print(c_arr, COUNT);
return 0;
}
計時炸彈終于被引爆了:
可見程序無法找到b_arr[1]確切位置,其后果不會好過你在街上把一個背影看似相熟的女性誤認(rèn)為是你的女朋友時所產(chǎn)生的尷尬。至于產(chǎn)生這個問題的原因,Scott Meyers已經(jīng)在他的《More Effective C++》作了詳細(xì)的講解[1],這里我就沒必要重復(fù)勞動了。
顯然,產(chǎn)生這個問題的根本原因是數(shù)組里的存放物長度不一致,如果能夠讓存放物的長度統(tǒng)一起來,問題就會迎刃而解了。問題男提出把指針放進(jìn)數(shù)組,好吧,現(xiàn)在我們來看一下C++中多態(tài)與數(shù)組牽手的景象:
// Code #02'
// See Code #02 for Class A, Class B and Class C.
void Print(A *arr[], int count)
{
for (int i = 0; i < count; ++i)
{
arr[i]->Print();
}
}
int _tmain(int argc, _TCHAR* argv[])
{
const int COUNT = 11;
A** a_arr = new A*[COUNT];
B** b_arr = new B*[COUNT];
C** c_arr = new C*[COUNT];
for (int i = 0; i < COUNT; ++i)
{
a_arr[i] = new A;
b_arr[i] = new B;
b_arr[i]->L = 141214121412;
c_arr[i] = new C;
c_arr[i]->D = 3.141592654;
}
Print(a_arr, COUNT);
Print(reinterpret_cast< A** >(b_arr), COUNT);
Print(reinterpret_cast< A** >(c_arr), COUNT);
return 0;
}
問題解決了,不過,我還是強(qiáng)烈建議你(當(dāng)然,你有選擇!):
在C++中,請盡量使用STL中的容器類來協(xié)助實現(xiàn)多態(tài)的效果。
3. ... In C++/CLI,
在C++/CLI的托管類型(Managed Type)中,只有ref class(或ref struct)[2]可用于建立繼承體系。下面,我們來看看在C++/CLI中使用數(shù)組實現(xiàn)多態(tài)的情形:
// Code #03
using namespace System;
using namespace stdcli::language;
ref class A
{
public:
virtual void Print()
{
Console::WriteLine("A");
}
};
ref class B : public A
{
public:
virtual void Print() override
{
Console::WriteLine("B");
}
long L;
};
ref class C : public A
{
public:
virtual void Print() override
{
Console::WriteLine("C");
}
double D;
};
void Print(array< A^ >^ arr)
{
for (int i = 0; i < arr->Count; ++i)
{
arr[i]->Print();
}
}
int _tmain()
{
const int COUNT = 11;
array< A^ >^ a_arr = gcnew array< A^ >(COUNT);
for (int i = 0; i < a_arr->Count; ++i)
{
a_arr[i] = gcnew A;
}
Print(a_arr);
array< B^ >^ b_arr = gcnew array< B^ >(COUNT);
for (int i = 0; i < b_arr->Count; ++i)
{
b_arr[i] = gcnew B;
b_arr[i]->l = 141214121412;
}
Print(b_arr);
array< C^ >^ c_arr = gcnew array< C^ >(COUNT);
for (int i = 0; i < c_arr->Count; ++i)
{
c_arr[i] = gcnew C;
c_arr[i]->d = 3.141592654;
}
Print(c_arr);
}
4. ... In C#,
而在C#中,使用數(shù)組來實現(xiàn)多態(tài)又是如何的呢:
// Code #04
using System;
class A
{
public virtual void Print()
{
Console.WriteLine("A");
}
}
class B : A
{
public override void Print()
{
Console.WriteLine("B");
}
public long L;
}
class C : A
{
public override void Print()
{
Console.WriteLine("C") ;
}
public double D;
}
class Program
{
static void Main(string[] args)
{
const int COUNT = 11;
A[] a_arr = new A[COUNT];
for (int i = 0; i < a_arr.Length; i++)
{
a_arr[i] = new A();
}
Print(a_arr);
B[] b_arr = new B[COUNT];
for (int i = 0; i < b_arr.Length; i++)
{
b_arr[i] = new B();
b_arr[i].L = 141214121412;
}
Print(b_arr);
C[] c_arr = new C[COUNT];
for (int i = 0; i < c_arr.Length; i++)
{
c_arr[i] = new C();
c_arr[i].D = 3.141592654;
}
Print(c_arr);
}
public static void Print(A[] arr)
{
for (int i = 0; i < arr.Length; ++i)
arr[i].Print();
}
}
5. 牽手的條件
其實,多態(tài)能夠牽上數(shù)組的手的條件很簡單:平等。回顧上面所有的代碼,我們不難發(fā)現(xiàn),數(shù)組中的存放物等長是完成多態(tài)效果的必要條件,否則,編譯器將被誤導(dǎo)繼而生成有問題的程序。
6. 關(guān)于舞臺
Code #01、Code #02和Code #03都是使用Visual C++ 2005 Express Edition Beta 1來編輯和編譯的,其中Code #01和Code #02是標(biāo)準(zhǔn)的Win32 Console Application,而Code #03是.NET Console Application。Code #04是使用Visual C# 2005 Express Edition Beta 1來編輯和編譯的。




