Aracılığıyla paylaş


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 CObjecttüretilir. CObject herhangi bir üye verisi yoktur, ancak bazı varsayılan işlevlere sahiptir. MI kullandığınızda, genellikle iki veya daha fazla CObjecttü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 CObListiç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 CFrameWndyapacaktır, ancak hakkında CObListhiç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 CWndtüretilmiş temel sınıf olmalıdır.

  • CWndTü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 CFrameWndtü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;

Ayrıca bkz.

Sayıya Göre Teknik Notlar
Kategoriye Göre Teknik Notlar