TN016: MFC'de C++ Birden Çok Devralmayı Kullanma
Bu not, Microsoft Foundation Sınıfları ile birden çok devralmayı (MI) kullanmayı açıklar. MFC ile MI kullanımı gerekli değildir. MI hiçbir MFC sınıfında kullanılmaz ve sınıf kitaplığı yazmak için gerekli değildir.
Aşağıdaki alt konu başlığında, MI'nin yaygın MFC deyimlerinin kullanımını nasıl etkilediği ve MI kısıtlamalarının bazılarını nasıl kapsadığı açıklanmaktadır. Bu kısıtlamalardan bazıları genel C++ kısıtlamalarıdır. Diğerleri MFC mimarisi tarafından dayatılır.
Bu teknik notun sonunda MI kullanan eksiksiz bir MFC uygulaması bulacaksınız.
Cruntimeclass
MFC'nin kalıcılık ve dinamik nesne oluşturma mekanizmaları, sınıfları benzersiz olarak tanımlamak için CRuntimeClass veri yapısını kullanır. MFC, bu yapılardan birini uygulamanızdaki her dinamik ve/veya serileştirilebilir sınıfla ilişkilendirir. Bu yapılar, uygulama türünde AFX_CLASSINIT
özel bir statik nesne kullanılarak başlatıldığında başlatılır.
uygulamasının CRuntimeClass
geçerli uygulaması MI çalışma zamanı türü bilgilerini desteklemiyor. Bu, MFC uygulamanızda MI kullanamayacağınız anlamına gelmez. Ancak, birden fazla temel sınıfa sahip nesnelerle çalışırken belirli sorumluluklara sahip olursunuz.
CObject::IsKindOf yöntemi, birden çok temel sınıfı varsa nesnenin türünü doğru şekilde belirlemez. Bu nedenle, CObject'i bir sanal temel sınıf olarak kullanamazsınız ve CObject::Serialize ve CObject::operator new gibi üye işlevlerine CObject
yapılan tüm çağrıların, C++ tarafından uygun işlev çağrısını kesinleştirebilmesi için kapsam niteleyicileri olmalıdır. Bir program MFC içinde MI kullandığında, temel sınıfı içeren sınıfın CObject
temel sınıf listesinde en soldaki sınıf olması gerekir.
Alternatif olarak işlecini kullanabilirsiniz dynamic_cast
. MI içeren bir nesneyi temel sınıflarından birine atama, derleyiciyi sağlanan temel sınıftaki işlevleri kullanmaya zorlar. Daha fazla bilgi için bkz . dynamic_cast İşleci.
CObject - Tüm Sınıfların Kökü
Tüm önemli sınıflar doğrudan veya dolaylı olarak sınıfından CObject
türetilir. CObject
herhangi bir üye verisi yoktur, ancak bazı varsayılan işlevlere sahiptir. MI kullandığınızda, genellikle iki veya daha fazla CObject
türetilmiş sınıftan devralırsınız. Aşağıdaki örnekte, bir sınıfın CFrameWnd ve CObList'ten nasıl devralabileceği gösterilmektedir:
class CListWnd : public CFrameWnd, public CObList
{
// ...
};
CListWnd myListWnd;
Bu durumda CObject
iki kez dahil edilir. Başka bir deyişle, yöntemlere veya işleçlere yapılan tüm başvuruları belirsiz hale getirmek için bir yönteme CObject
ihtiyacınız vardır. new işleci ve işleç silme, kesinleştirmesi gereken iki işleçtir. Başka bir örnek olarak, aşağıdaki kod derleme zamanında bir hataya neden olur:
myListWnd.Dump(afxDump); // compile time error, CFrameWnd::Dump or CObList::Dump
CObject Yöntemlerini Yeniden Birleştirme
İki veya daha fazla CObject
türetilmiş temel sınıfı olan yeni bir sınıf oluşturduğunuzda, diğer kişilerin kullanmasını istediğiniz yöntemleri yeniden CObject
oluşturmanız gerekir. delete
ve işleçleri new
zorunludur ve Döküm önerilir. Aşağıdaki örnek, ve işleçlerini new
ve yöntemini yeniden sunarDump
:delete
class CListWnd : public CFrameWnd, public CObList
{
public:
void* operator new(size_t nSize)
{
return CFrameWnd:: operator new(nSize);
}
void operator delete(void* p)
{
CFrameWnd:: operator delete(p);
}
void Dump(CDumpContent& dc)
{
CFrameWnd::Dump(dc);
CObList::Dump(dc);
}
// ...
};
CObject Sanal Devralma
Sanal olarak devralma CObject
işlevin belirsizliği sorununu çözebilir gibi görünebilir, ancak durum böyle değildir. içinde CObject
üye veri olmadığından, temel sınıf üye verilerinin birden çok kopyasını önlemek için sanal devralmaya ihtiyacınız yoktur. Daha önce gösterilen ilk örnekte, Dump
ve CObList
içinde farklı CFrameWnd
uygulandığından sanal yöntem hala belirsizdir. Belirsizliği kaldırmanın en iyi yolu, önceki bölümde sunulan önerileri izlemektir.
CObject::IsKindOf ve Çalışma Zamanı Yazma
içinde MFC CObject
tarafından desteklenen çalışma zamanı yazma mekanizması DECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIAL ve IMPLEMENT_SERIAL makroları kullanır. Bu makrolar, güvenli aşağı yayınları garanti etmek için bir çalışma zamanı türü denetimi gerçekleştirebilir.
Bu makrolar yalnızca tek bir temel sınıfı destekler ve devralınan sınıfları çarpmak için sınırlı bir şekilde çalışır. IMPLEMENT_DYNAMIC veya IMPLEMENT_SERIAL belirttiğiniz temel sınıf, ilk (veya en soldaki) temel sınıf olmalıdır. Bu yerleştirme, yalnızca en soldaki temel sınıf için tür denetimi yapmanızı sağlar. Çalışma zamanı türü sistemi ek temel sınıflar hakkında hiçbir şey bilmez. Aşağıdaki örnekte, çalışma zamanı sistemleri üzerinde tür denetimi CFrameWnd
yapacaktır, ancak hakkında CObList
hiçbir şey bilmeyecektir.
class CListWnd : public CFrameWnd, public CObList
{
DECLARE_DYNAMIC(CListWnd)
// ...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)
CWnd ve İleti Haritalar
MFC ileti eşleme sisteminin düzgün çalışması için iki ek gereksinim vardır:
Yalnızca bir
CWnd
türetilmiş temel sınıf olmalıdır.CWnd
Türetilmiş temel sınıf ilk (veya en soldaki) temel sınıf olmalıdır.
İşte işe yaramayan bazı örnekler:
class CTwoWindows : public CFrameWnd, public CEdit
{ /* ... */ }; // error : two copies of CWnd
class CListEdit : public CObList, public CEdit
{ /* ... */ }; // error : CEdit (derived from CWnd) must be first
MI kullanan örnek bir program
Aşağıdaki örnek, ve CWinApp'ten CFrameWnd
türetilen bir sınıftan oluşan tek başına bir uygulamadır. Bir uygulamayı bu şekilde yapılandırmanızı önermeyiz, ancak bu bir sınıfa sahip en küçük MFC uygulaması örneğidir.
#include <afxwin.h>
class CHelloAppAndFrame : public CFrameWnd, public CWinApp
{
public:
CHelloAppAndFrame() {}
// Necessary because of MI disambiguity
void* operator new(size_t nSize)
{ return CFrameWnd::operator new(nSize); }
void operator delete(void* p)
{ CFrameWnd::operator delete(p); }
// Implementation
// CWinApp overrides
virtual BOOL InitInstance();
// CFrameWnd overrides
virtual void PostNcDestroy();
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CHelloAppAndFrame, CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
// because the frame window is not allocated on the heap, we must
// override PostNCDestroy not to delete the frame object
void CHelloAppAndFrame::PostNcDestroy()
{
// do nothing (do not call base class)
}
void CHelloAppAndFrame::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect(rect);
CString s = "Hello, Windows!";
dc.SetTextAlign(TA_BASELINE | TA_CENTER);
dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
dc.SetBkMode(TRANSPARENT);
dc.TextOut(rect.right / 2, rect.bottom / 2, s);
}
// Application initialization
BOOL CHelloAppAndFrame::InitInstance()
{
// first create the main frame
if (!CFrameWnd::Create(NULL, "Multiple Inheritance Sample",
WS_OVERLAPPEDWINDOW, rectDefault))
return FALSE;
// the application object is also a frame window
m_pMainWnd = this;
ShowWindow(m_nCmdShow);
return TRUE;
}
CHelloAppAndFrame theHelloAppAndFrame;