Aracılığıyla paylaş


TN016: MFC ile C++ Birden Çok Devralmayı Kullanma

Uyarı

Microsoft Foundation Sınıfları (MFC) kitaplığına destek verilmeye devam ediliyor. Ancak artık özellik eklemeyeceğiz veya belgeleri güncelleştirmeyeceğiz.

Bu belge, Microsoft Foundation Sınıfları ile çoklu kalıtım (MI) kullanımını 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, AFX_CLASSINIT türünde özel bir statik nesne kullanılarak uygulama başlatıldığında oluşturulur.

CRuntimeClass 'nin mevcut sürümü, 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 ve CObject::operator new gibi üye işlevlerine 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 CObjecttü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. Bu, CObject yöntemlere veya işleçlere yapılan herhangi bir başvuruyu daha belirgin hale getirmek için bir yol bulmanız gerektiği anlamına gelir. new işleci ve delete işleci, ayırt edilmesi 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 uygulamanız gerekir. Operatörler new ve delete zorunludur ve Döküm önerilir. Aşağıdaki örnek, new ve delete işleçlerini ve Dump yöntemini yeniden sunar:

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'in Sanal Kalıtımı

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 CFrameWnd içinde farklı uygulanıp uygulanmadıklarından, CObList sanal yöntem hala belirsizdir. Belirsizliği kaldırmanın en iyi yolu, önceki bölümde sunulan önerileri izlemektir.

CObject::IsKindOf ve Run-Time 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 konumlandırma, 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 CFrameWnd üzerinde tür denetimi yapacak, ancak CObList hakkında hiçbir şey bilmeyecek.

class CListWnd : public CFrameWnd, public CObList
{
    DECLARE_DYNAMIC(CListWnd)
    // ...
};
IMPLEMENT_DYNAMIC(CListWnd, CFrameWnd)

CWnd ve İleti Eşlemeleri

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 ile 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, CFrameWnd ve CWinApp'ten türetilen bir sınıftan oluşan bağımsız 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;

Ayrıca bakınız

Numaraya Göre Teknik Notlar
Kategorilere Göre Teknik Notlar