TN016: การใช้ c ++หลายสืบทอดกับ MFC

หมายเหตุนี้อธิบายวิธีใช้หลายสืบทอด (MI) กับคลาสที่ Microsoft Foundation?

ทำไมหลายสืบทอด?

มีการอภิปรายอย่างต่อเนื่องใน c ++และชุมชนเชิงวัตถุผ่านค่าของ MI Visual c ++ compiler และพัฒนาสภาพแวดล้อมทั้งหมดสนับสนุน MI?

รีคลาส MFC ได้ถูกออกแบบมาเพื่อให้คุณไม่จำเป็นต้องเข้าใจ MI ใช้ MFC ไม่มีใช้ MI ในคลาสที่ MFC เราได้พบว่า MI ไม่จำเป็นต้องเขียนไลบรารีคลาส ไม่จำเป็นจะต้องมีสำหรับการเขียนโปรแกรมประยุกต์ที่ร้ายแรง การใช้ MI หรือไม่สามารถตัดสินใจส่วนตัว ดังนั้นเราปล่อยให้ตัดสินใจว่าคุณ?

ดังนั้นคุณต้องการใช้ MI?

ถ้าคุณได้เข้าใจวิธีการใช้ MI ทำความเข้าใจเกี่ยวกับการค้าไม่ชอบจากประสิทธิภาพการทำงาน และต้องการใช้ MFC, technote นี้จะบอกสิ่งที่คุณต้องทำ บางส่วนของข้อจำกัดที่มีข้อจำกัดของ c ++ทั่วไป ผู้อื่นถูกกำหนด โดยสถาปัตยกรรม MFC?

ต่อไปนี้อธิบายปัญหาทางเทคนิคของวิธี MI ส่งผลกระทบต่อการใช้สำนวน MFC ทั่วไปบางอย่าง ในตอนท้ายของหมายเหตุนี้ทางด้านเทคนิคโปรแกรมประยุกต์ MFC ที่สมบูรณ์โดยใช้ MI มีอยู่ซึ่ง คุณสามารถแยก และคอมไพล์?

CRuntimeClass

การเก็บรักษาและกลไกการสร้างวัตถุแบบไดนามิกของ MFC ใช้โครงสร้างข้อมูลCRuntimeClassในการระบุคลาสของ MFC เชื่อมโยงโครงสร้างหนึ่งชนิดนี้กับแต่ละชนชั้นแบบไดนามิก และ/หรือ serializable ในแอพลิเคชัน โครงสร้างเหล่านี้กำลังเตรียมใช้งานโปรแกรมประยุกต์เริ่มต้นเวลาใช้วัตถุแบบคงพิเศษชนิดAFX_CLASSINIT คุณต้องไม่เกี่ยวกับตัวคุณเอง ด้วยการใช้งานข้อมูลนี้ เป็นก็มีแนวโน้มการเปลี่ยนแปลงระหว่างการแก้ไขของ MFC?

การใช้งานCRuntimeClassปัจจุบันไม่สนับสนุนการสืบทอดหลาย runtime ชนิดข้อมูล ความหมายนี้ไม่ว่า คุณไม่สามารถใช้ MI ในโปรแกรมประยุกต์ของคุณ MFC แต่ถ้าคุณทำได้ คุณจะต้องรับผิดชอบบางอย่างเมื่อทำงานกับวัตถุที่มีคลาสพื้นฐานที่มากกว่าหนึ่ง?

CObject::IsKindOfสมาชิกฟังก์ชันจะไม่ถูกต้องกำหนดชนิดของวัตถุหากมีหลายฐานชั้น ดังนั้นคุณไม่สามารถใช้CObjectเป็นคลาสพื้นฐานเสมือนจริง และเรียกฟังก์ชันสมาชิกCObjectเช่นSerializeและตัวดำเนินการใหม่ทั้งหมดจะต้องมีตัวระบุขอบเขตดังนั้นที่ c ++สามารถ disambiguate การเรียกฟังก์ชันที่เหมาะสม ถ้าคุณค้นหาความต้องการใช้ MI ภายใน MFC แล้วคุณควรแน่ใจว่าทำชั้นประกอบด้วยคลาสพื้นฐานของCObjectคลาซ้ายสุดในรายการของฐานชั้น?

สำหรับคำแนะนำในการใช้และการฉ้อ MI ดูขั้นสูง c ++ Programming ลักษณะและสำนวนโดย James O. Coplien (Addison เวสลีย์ 1992)?

CObject - รากของชั้นเรียนทั้งหมด

สืบทอดอย่างที่ทราบ ชั้นที่สำคัญทั้งหมดมาทั้งทางตรง หรือทางอ้อมจากคลาสCObject ไม่มีข้อมูลใด ๆ ที่สมาชิกCObjectแต่มีการทำงานเริ่มต้นบางอย่าง เมื่อใช้ MI จะสืบทอดจากสอง หรือมากกว่าสองCObjectทั่วไป-มาเรียน ตัวอย่าง การCFrameWndและCObList:

คลา CListWnd: สาธารณะ CFrameWnd, CObList สาธารณะ
{
 ...
};
CListWnd myListWnd

ในกรณีนี้CObjectมีอยู่สอง ซึ่งนำไปสองปัญหา:

ขั้นตอนที่แนะนำ

เมื่อสร้างคลาสใหม่ด้วยอย่าง น้อยสองCObjectมาเรียนพื้นฐาน reimplement เหล่าสมาชิกCObjectที่คุณต้องการให้ผู้ใช้สามารถใช้ แนะนำตัวดำเนินการใหม่และลบเป็นข้อบังคับถ่ายโอนข้อมูลให้ ตัวอย่างเช่น:

คลา CListWnd: สาธารณะ CFrameWnd, CObList สาธารณะ
{
สาธารณะ:
 nbsp  โมฆะ * ตัวใหม่ (size_t nSize)
        {กลับ CFrameWnd::operator new(nSize); }
    ตัวดำเนินการเลิกลบ (โมฆะ * p)
        {CFrameWnd::operator delete(p); }

โมฆะการถ่ายโอนข้อมูล (CDumpContent & dc)
        {CFrameWnd::Dump(dc)
          CObList::Dump(dc) }
     ...
}

สืบทอดเสมือนของ CObject?

คุณอาจขอให้ "ถ้าคุณทอด CObject ช่วย จะไม่ปัญหาโปรแกรมทั้งหมดหายไป?

แม้ในโมเดลวัตถุมีประสิทธิภาพของ Microsoft สืบทอดเสมือนไม่ได้เป็นประสิทธิภาพเป็นการสืบทอดไม่ใช่เสมือน (เช่นเดียวกับการสืบทอดหลายไม่มีประสิทธิภาพเป็นสิ่งที่สืบทอดเดียวในบางกรณี) เนื่องจากไม่มีข้อมูลสมาชิกในCObjectสืบทอดเสมือนไม่จำเป็นเพื่อป้องกันไม่ให้ข้อมูลสมาชิกของคลาส base หลายสำเนา?

คำตอบที่แท้จริงคือไม่มี สืบทอดเสมือนจะไม่สามารถแก้ไขปัญหาโปรแกรมที่แสดงอยู่ข้างบน ตัวอย่าง:การถ่ายโอนข้อมูลเสมือนสมาชิกฟังก์ชันจะยังคงคลุมเครือ (เนื่องจากCFrameWndและCObListใช้แตกต่างกัน)?

ดังนั้น เราขอแนะนำตามขั้นตอนข้างต้นเพื่อให้การแก้ความกำกวม:

CObject::IsKindOf และเวลาทำงานพิมพ์

กลไกพิมพ์ขณะใช้งานจริงได้รับการสนับสนุน โดย MFC ในCObjectใช้แมโคDECLARE_DYNAMIC, IMPLEMENT_DYNAMIC, DECLARE_DYNCREATE, IMPLEMENT_DYNCREATE, DECLARE_SERIALและIMPLEMENT_SERIAL ข้อผิดพลาดเหล่านี้ให้สามารถทำการตรวจสอบชนิดขณะทำการอนุญาตสำหรับเซฟนำแสดงโดย-downs?

แมโครเหล่านี้สนับสนุนคลาสพื้นฐานเดียวเท่านั้น และจะทำงานในแบบจำกัดสำหรับคลาสที่สืบทอดมาคูณ คลาสพื้นฐานคุณระบุในIMPLEMENT_DYNAMICหรือIMPLEMENT_SERIALควรชั้นฐานแรก (หรือซ้ายสุด) ตัวอย่างเช่น,

คลา CListWnd: สาธารณะ CFrameWnd, CObList สาธารณะ
{
 nbsp  DECLARE_DYNAMIC(CListWnd)
    ...
};
IMPLEMENT_DYNAMIC (CListWnd, CFrameWnd)(&N)

นี้จะช่วยให้คุณพิมพ์การตรวจสอบสำหรับซ้ายสุดพื้นฐานชั้นเท่านั้น ระบบชนิดเวลาทำงานจะทราบอะไรเกี่ยวกับเพิ่มเติมฐาน (CObListในกรณีนี้)?

CWnd และแผนที่ของข้อความ

ในใบสั่งสำหรับ MFC ข้อความแผนผังระบบการทำงานอย่างถูกต้อง ไม่มีข้อกำหนดเพิ่มเติมที่สอง:

ในตัวอย่างข้างต้นCFrameWndคือชั้นเบสแรก?

บางตัวอย่างที่จะไม่ทำงาน:

คลา CTwoWindows: สาธารณะ CFrameWnd, CEdit สาธารณะ
 nbsp  { ... };
        / / ข้อผิดพลาด: สองสำเนา CWnd

คลา CListEdit: สาธารณะ CObList, CEdit สาธารณะ
    { ... };
        / / ข้อผิดพลาด: CEdit (มาจาก CWnd) ต้องเป็นแรก(&N)

ตัวอย่างโปรแกรมที่ใช้ MI

ตัวอย่างต่อไปนี้เป็นโปรแกรมประยุกต์แบบสแตนด์อโลนที่ประกอบด้วยชั้นหนึ่งที่ได้รับมาจากCFrameWndและCWinApp ด้วยวิธีการของการจัดโครงสร้างของโปรแกรมประยุกต์นี้ไม่ใช่การแนะนำ แต่นี่คือตัวอย่างของโปรแกรมประยุกต์ MFC น้อยที่สุดด้วยหนึ่งคลาส?

คุณสามารถตัดโปรแกรมต่อไปนี้ และคัดลอกอยู่ด้านบนของ HELLOAPPCPP ตัวอย่างทั่วไปของ MFC เดียวสืบทอด HELLOAPP สร้างโปรแกรมเช่นปกติ?

#include <afxwin.h>

class CHelloAppAndFrame : public CFrameWnd, public CWinApp
{ 
public:
    CHelloAppAndFrame()
        { }

    // Necessary evil for 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()

// since 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;

หมายเหตุด้านเทคนิคตามหมายเลข|nbsp หมายเหตุด้านเทคนิคตามประเภท(&N)

Index