หมายเหตุนี้อธิบายวิธีใช้หลายสืบทอด (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มีอยู่สอง ซึ่งนำไปสองปัญหา:
myListWnd.Dump(afxDump)
nbsp / / compile error เวลา CFrameWnd::Dump หรือ CObList::Dump(&N)
ขั้นตอนที่แนะนำ
เมื่อสร้างคลาสใหม่ด้วยอย่าง น้อยสอง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)