C++程序設(shè)計(jì)

前面的三章几缭,我們說了多態(tài)的一些技術(shù)內(nèi)幕還有一些關(guān)于C++對(duì)象模型的內(nèi)容想许,所以我就在想是要繼續(xù)深入C++的知識(shí)點(diǎn)呢還是就目前的內(nèi)容我們來聊聊如何來設(shè)計(jì)一個(gè)應(yīng)用程序伶授,最后選擇了后者,這一章的內(nèi)容我們來說說如何搭建一個(gè)GUI框架流纹,由于GUI框架涉及到方方面面糜烹,所以我們這里只能算是一個(gè)簡(jiǎn)單的切入點(diǎn),不涉及詳細(xì)的編碼的實(shí)現(xiàn)漱凝。

GUI框架很多疮蹦,在windows上面C++有MFC,WTL茸炒,還有跨平臺(tái)的Qt等等愕乎,我們可以隨便找一個(gè)來作為參考,有了參考之后我們還需要對(duì)我們的框架的模塊規(guī)劃壁公。

我們打算寫一個(gè)DirectUI框架感论, 所以我們需要一個(gè)窗口——CDxWindowWnd。

創(chuàng)一個(gè)小群紊册,供大家學(xué)習(xí)交流聊天

如果有對(duì)學(xué)C++方面有什么疑惑問題的比肄,或者有什么想說的想聊的大家可以一起交流學(xué)習(xí)一起進(jìn)步呀。

也希望大家對(duì)學(xué)C++能夠持之以恒

C++愛好群囊陡,

如果你想要學(xué)好C++最好加入一個(gè)組織芳绩,這樣大家學(xué)習(xí)的話就比較方便,還能夠共同交流和分享資料撞反,給你推薦一個(gè)學(xué)習(xí)的組織:快樂學(xué)習(xí)C++組織 可以點(diǎn)擊組織二字妥色,可以直達(dá)


CDxWindowWnd,作為我們的基本窗口類痢畜,該類我們只需要對(duì)HWND進(jìn)行簡(jiǎn)單的包裝垛膝。

//+--------------------------

//

// class CDxWindowWnd

// windows的基本窗口、

//

//

class CDxWindowWnd{

public:

CDxWindowWnd();

virtual ~CDxWindowWnd();

operator HWND() const;

void ShowWindow(bool bShow = true, bool bTakeFocus = true);

UINT ShowModal();

virtual void CloseHwnd(UINT nRet = IDOK);

void CenterWindow();

protected:

virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);

private:

HWND m_Hwnd{nullptr};

};

//+-------------------------------

窗口有了丁稀,我們需要一個(gè)消息循環(huán)吼拥,對(duì)于windows應(yīng)用程序來說寫個(gè)消息循環(huán)很簡(jiǎn)單:

//+--------------------------------

MSG msg = { 0 };

while (::GetMessage(&msg, NULL, 0, 0)) {

::TranslateMessage(&msg);

::DispatchMessage(&msg);

if (msg.message == WM_CLOSE && ::GetParent(msg.hwnd) == NULL)

break;

}

//+-------------------------------

雖然這幾句代碼可以實(shí)現(xiàn)我們所要的消息循環(huán),但是我們可以將該消息循環(huán)進(jìn)行封裝线衫,并且將該模塊作為單例凿可,這樣一樣我們可以在里面進(jìn)行更多的操作:

//+-------------------------------

//

// class CDxApplication?

// 負(fù)責(zé)窗口程序的消息循環(huán)

// 以即一些公有資料的管理

//

class CDxApplication{

public:

CDxApplication(HINSTANCE __hInstance = nullptr);

~CDxApplication();

static CDxApplication* InstanceWithArgs(HINSTANCE __hInstance);

static CDxApplication* Instance();

static void Destroy();

static void SetFont(const MString& fontName, unsigned fSize, bool isBold = false, bool isItalic = false);

static HINSTANCE GetHInstance();

static HFONT GetFont();

static DXFontInfo GetFontInfo();

static MString GetExePath();

static MString GetCurrentTime();

static MString GetUUIDStr();

//

// 消息循環(huán)

//

void Quit();

int Run();

protected:

static CDxApplication* ?__sPtr;

};

//

// 現(xiàn)在我們可以直接創(chuàng)建窗口并顯示出來

//

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow)

{

DxUI::CDxApplication* App = DxUI::CDxApplication::InstanceWithArgs(hInstance);

DxUI::CDxWindowWnd WndWindow;

WndWindow.Create(nullptr, L"TestWindow", DXUI_WNDSTYLE_FRAME, 0);

WndWindow.ShowWindow();

App->Run();

DxUI::CDxApplication::Destroy();

return 0;

}

//+--------------------------------

窗口我們創(chuàng)建出來了,但不符我們的DirectUI的預(yù)期,我們需要將標(biāo)題欄去掉枯跑,所以我們可以在CDxWindowWnd的基礎(chǔ)上進(jìn)一步修改:

//+--------------------------------

class CDxWindowImpl : public CDxWindowWnd

{

public:

CDxWindowImpl();

~CDxWindowImpl();

virtual LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

virtual LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

virtual LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

virtual LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

virtual LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);

LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); // 從基類基礎(chǔ)而來

//

// 其他

//

protected:

int mIdentifyId{ DX_InvalidID };

bool bIsVisible{ true };

bool bIsZoomable{ true };

RECT mCaptionBox;

RECT mSizeBox;

SIZE mMaxSize;

SIZE mMinSize;

SIZE mRoundRectSize;

};

//+-------------------------------

修改窗口風(fēng)格我們放在OnCreate函數(shù)中進(jìn)行實(shí)現(xiàn):

//+------------------------------

LRESULT CDxWindowImpl::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

//+-------------------

//

// 調(diào)整窗口樣式

//

//+--------------------

LONG styleValue = ::GetWindowLong(*this, GWL_STYLE);

styleValue &= ~WS_CAPTION;

::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);

return 0;

}

//+------------------------------

當(dāng)然惨驶,如果我們就這樣把標(biāo)題欄去掉之后,窗口就沒法拉動(dòng)敛助,也沒法關(guān)閉粗卜,就一直停在桌面上,一動(dòng)不動(dòng)纳击,所以為了解決這個(gè)問題续扔,我們必須換種方式把標(biāo)題欄給重新繪制出來,這就是 OnNcHitTest 的功勞了纱昧。

//+-----------------------------

LRESULT CDxWindowImpl::OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

POINT pt;

pt.x = GET_X_LPARAM(lParam);

pt.y = GET_Y_LPARAM(lParam);

::ScreenToClient(*this, &pt);

RECT rcClient;

::GetClientRect(*this, &rcClient);

if (!::IsZoomed(*this) && bIsZoomable)

{

RECT rcSizeBox = mSizeBox;

if (pt.y < rcClient.top + rcSizeBox.top)

{

if (pt.x < rcClient.left + rcSizeBox.left) return HTTOPLEFT;

if (pt.x > rcClient.right - rcSizeBox.right) return HTTOPRIGHT;

return HTTOP;

}

else if (pt.y > rcClient.bottom - rcSizeBox.bottom)

{

if (pt.x < rcClient.left + rcSizeBox.left) return HTBOTTOMLEFT;

if (pt.x > rcClient.right - rcSizeBox.right) return HTBOTTOMRIGHT;

return HTBOTTOM;

}

if (pt.x < rcClient.left + rcSizeBox.left) return HTLEFT;

if (pt.x > rcClient.right - rcSizeBox.right) return HTRIGHT;

}

RECT rcCaption = mCaptionBox;

if (-1 == rcCaption.bottom)

{

rcCaption.bottom = rcClient.bottom;

}

if (pt.x >= rcClient.left + rcCaption.left && pt.x < rcClient.right - rcCaption.right

&& pt.y >= rcCaption.top && pt.y < rcCaption.bottom)

{

return HTCAPTION;

}

return HTCLIENT;

}

//+-------------------------------------

標(biāo)題欄的關(guān)鍵在于mCaptionBox的bottom的值,所以我們可以設(shè)置mCaptionBox來修改標(biāo)題欄的高度洒扎。

我們現(xiàn)在雖然得到了我們想要的無標(biāo)題欄窗口邓线,但是這只是一張白板,所以我們還需要對(duì)齊進(jìn)行繪制,我們稱該層為繪制資源管理層:

//+-------------------------------------

class CDxRendImpl : public CDxWindowImpl

{

public:

CDxRendImpl();

~CDxRendImpl();

virtual bool OnInitResource2D();

virtual bool OnInitResource3D();

virtual void UnInitResource();

virtual void OnRender();

virtual void OnRender2D();

virtual void OnRender3D();

virtual void SaveToFile(const MString& fileName);

virtual void OnRendWindow(IPainterInterface* painter);

};

//+--------------------------------------

我們想要繪制那么我們就需要一個(gè)繪制模塊,繪制的時(shí)候我們還需要效果挡篓,所以我們還需要兩個(gè)模塊:

//+-------------------------------------

//

// 效果接口

//

class CDxEffects

{

///

/// 多種效果

///

};

//

// 二維平面變換矩陣

//

struct TransformMatrix{

FLOAT _11;

FLOAT _12;

FLOAT _21;

FLOAT _22;

FLOAT _31;

FLOAT _32;

};

//

// 繪圖接口

//

class IPainterInterface{

public:

//+-------------------------------------------

//

// 為繪制方便官研,下面的接口都得實(shí)現(xiàn)

// 當(dāng)然如果實(shí)際沒有用處的可以簡(jiǎn)單的實(shí)現(xiàn)即可

//

//+-------------------------------------------

virtual ~IPainterInterface(){};

virtual void BeginDraw() = 0;? // 開始繪制

virtual void Clear(const DxColor& col) = 0; // 使用特定色彩擦除背景

virtual void EndDraw() = 0;? // 結(jié)束繪制

virtual void DrawRectangle(const RECT& rc, const DxColor& col,double size) = 0;

virtual void DrawRoundedRectangle(const RECT& rc, const SIZE& radius, const DxColor& col, double size) = 0;

virtual void DrawEllipse(const RECT& rc, const SIZE& radius, const DxColor& col, double size) = 0;

virtual void DrawDashRectangle(const RECT& rc, const DxColor& col, double size) = 0;

virtual void DrawDashRoundedRectangle(const RECT& rc, const SIZE& radius, const DxColor& col, double size) = 0;

virtual void DrawDashEllipse(const RECT& rc, const SIZE& radius, const DxColor& col, double size) = 0;

virtual void FillRectangle(const RECT& rc, const DxColor& col) = 0;

virtual void FillRoundedRectangle(const RECT& rc, const SIZE& radius, const DxColor& col) = 0;

virtual void FillEllipse(const RECT& rc, const SIZE& radius, const DxColor& col) = 0;

virtual void FillRectangle(const RECT& rc, CDxEffects* pEffects) = 0;

virtual void FillRoundedRectangle(const RECT& rc, const SIZE& radius, CDxEffects* pEffects) = 0;

virtual void FillEllipse(const RECT& rc, const SIZE& radius, CDxEffects* pEffects) = 0;

virtual void DrawBitmap(const RECT& rc, CDxEffects* pEffects) = 0;

virtual void DrawBitmap(const RECT& rc, const MString& bitmap,int w = -1,int h = -1) = 0;

virtual void DrawText(const MString& Text, const RECT& rc, CDxEffects* pEffects) = 0;? // 只繪制文本,超出區(qū)域不管 效率更高

virtual void DrawText(const MString& Text, const RECT& rc, const DxColor& col, const DXFontInfo& font,DXAlignment alignt) = 0; // 不使用效果直接繪制

virtual void DrawTextWithClip(const MString& Text, const RECT& rc, CDxEffects* pEffects, const RECT& cliprc = { 0, 0, 0, 0 }) = 0; // 繪制文本闯睹,超出區(qū)域裁剪

virtual void BeginClipRect(const RECT& rc) = 0;? // 在繪制之前調(diào)用

virtual void BeginClipRect(const std::vector& points) = 0;? // 在繪制之前調(diào)用

virtual void EndClipRect() = 0; // 在繪制完成之后調(diào)用

//

// 繪制線體

// DrawLines效率更高

//

virtual void DrawLine(const DxPointD& first, const DxPointD& second, const DxColor& col, double Size) = 0;

virtual void DrawLines(const std::vector& points, const DxColor& col, double Size) = 0;

virtual void DrawDashLine(const DxPointD& first, const DxPointD& second, const DxColor& col, double Size) = 0;

virtual void DrawDashLines(const std::vector& points, const DxColor& col, double Size) = 0;

//

// 變換

//

virtual void SetTransform(const TransformMatrix& mat) = 0;

};

//+---------------------------

IPainterInterface?是一個(gè)純虛類戏羽,換句話說就是接口類,該類沒有對(duì)他的子類提供任何便利楼吃,反而要求子類必須實(shí)現(xiàn)自己定義的所有純虛接口始花,而所謂純虛接口就是虛函數(shù)等于0的函數(shù),而只要含有純虛函數(shù)的類就是純虛類孩锡,也就是所謂的接口類酷宵,之所以我們這里要將繪制操作定義為純虛類主要是考慮到以后可能會(huì)使用不同的圖像引擎來繪制圖形,比如我們這里可以使用D2D躬窜,也可以使用OpenGL浇垦,還可以使用GDI等等,那么我們?yōu)槭裁茨軌蛲瑫r(shí)展示二維和三維圖形荣挨,所以我們選擇D2D:

//+---------------------------

class CDxPainter : public IPainterInterface

{

public:

typedef ID2D1RenderTarget* LPID2D1HwndRenderTarget;

public:

CDxPainter(ID2D1RenderTarget* render);

~CDxPainter();

ID2D1RenderTarget* getRenderTarget() const;

ID2D1RenderTarget* operator->() const;

operator LPID2D1HwndRenderTarget() const;

operator bool() const;

//

// 下面是 IPainterInterface 繼承而來的所有接口

//

private:

ID2D1RenderTarget* pRenderTarget;

CDxCharaterFormat* pTextRender{ nullptr };

ID2D1Layer* p_ClipLayout{ nullptr };

ID2D1Geometry* p_ClipGeometry{ nullptr };

};

//+------------------------------

到現(xiàn)在我們已經(jīng)擁有了窗口男韧,效果,繪圖三個(gè)模塊默垄,所以我們只需要將三個(gè)模塊組合起來就可以進(jìn)行圖形繪制此虑,這個(gè)操作我們放在CDxRendImpl::OnRender()中,不過CDxRendImpl::OnRender()中我們默認(rèn)繪制2D平面口锭,所以真正的操作我們放在CDxRendImpl::OnRender2D()中:

//+------------------------------

void CDxRendImpl::OnRender(){

OnRender2D();

}

void CDxRendImpl::OnRender2D(){

CDxPainter painter(pRendTarget);

painter.BeginDraw();

this->OnRendWindow(&painter);

painter.EndDraw();

}

void CDxRendImpl::OnRendWindow(IPainterInterface* painter){

;

}

//+-------------------------------

事實(shí)上我們并不對(duì)該層進(jìn)行渲染寡壮,所以該層的OnRendWindow函數(shù)被實(shí)現(xiàn)為空,因?yàn)槲覀冃枰氖亲鲆粋€(gè)DirectUI框架,而我們現(xiàn)在還是基于窗口HWND的况既,所以我們還差我們的DirectUI窗口这溅,當(dāng)然DirectUI至少需要一個(gè)持有HWND,所以我們必須繼承至CDxRendImpl.

//+-------------------------------

//

// DirectUI 窗口類

//

class CDxWidget : public? CDxRendImpl

{

public:

CDxWidget();

~CDxWidget();

//+---------------------------

//

// 創(chuàng)建Hwnd

//

//+---------------------------

virtual void CreateHwnd();

virtual void CreateHwnd(HWND parent);

//

// 其他

//

};

//+---------------------------------

我們可以通過CDxWidget::CreateHwnd()決定是否需要?jiǎng)?chuàng)建HWND棒仍,我們只對(duì)主窗口創(chuàng)建HWND對(duì)于子窗口不創(chuàng)建悲靴,在渲染的時(shí)候我們先渲染當(dāng)前窗口,再對(duì)子窗口進(jìn)行渲染莫其。

//+--------------------------------

//

// 繪制窗口

//

void CDxWidget::OnRendWindow(IPainterInterface* painter){

if (bIsVisible == false){

return;

}

mEffects.SetCurrentStatus(GetWindowStatus());

//+---------------

//

// 渲染Title

//

//+--------------

if (mHwnd && !pCaptionLabel&& !::GetParent(mHwnd)){

pCaptionLabel = new CDxCaption;

pCaptionLabel->SetParent(this);

RECT rc = mFrameArea;

rc.bottom =? mCaptionBox.bottom;

pCaptionLabel->SetGeomety(rc);

mRendArea = mFrameArea;

mRendArea.X(mFrameArea.X() + mSizeBox.left);

mRendArea.Y(mRendArea.Y() + mCaptionBox.bottom);

mRendArea.Width(mFrameArea.Width() - mSizeBox.left - mSizeBox.right);

mRendArea.Height(mFrameArea.Height() - mCaptionBox.bottom - mSizeBox.bottom);

if (!mIcon.empty()){

pCaptionLabel->GetIconEffects()->SetBitmaps(Dx_Normal, mIcon);

}

UpdateChildWindowPos();

}

if (pCaptionLabel){

RECT rc = mFrameArea;

rc.bottom = rc.top + pCaptionLabel->GetFrameRect().Height();

pCaptionLabel->SetGeomety(rc);

pCaptionLabel->SetText(mTitle);

pCaptionLabel->OnRendWindow(painter);

}

if (mEffects.GetEffectType() == CDxEffects::Dx_ImageType){

painter->DrawBitmap(mImageRendArea, &mEffects);

}

else if (mEffects.GetEffectType() == CDxEffects::Dx_ColorType){

DXShape shape = GetWindowShape();

switch (shape)

{

case DxUI::Dx_Rectangle:

painter->FillRectangle(mRendArea, &mEffects);

break;

case DxUI::Dx_RoundedRectangle:

painter->FillRoundedRectangle(mRendArea, mRoundRectSize, &mEffects);

if (bIsNeedBorder && mBorderWidth > 0){

painter->DrawRoundedRectangle(mRendArea, mRoundRectSize, mBorderColor, mBorderWidth);

}

break;

case DxUI::Dx_Ellipse:

painter->FillEllipse(mRendArea, mRoundRectSize, &mEffects);

break;

default:

break;

}

}

else{

DXShape shape = GetWindowShape();

switch (shape)

{

case DxUI::Dx_Rectangle:

painter->FillRectangle(mRendArea, &mEffects);

painter->DrawBitmap(mImageRendArea, &mEffects);

break;

case DxUI::Dx_RoundedRectangle:

painter->FillRoundedRectangle(mRendArea, mRoundRectSize, &mEffects);

painter->DrawBitmap(mImageRendArea, &mEffects);

break;

case DxUI::Dx_Ellipse:

painter->FillEllipse(mRendArea, mRoundRectSize, &mEffects);

painter->DrawBitmap(mImageRendArea, &mEffects);

break;

default:

break;

}

}

if (!mText.empty() ){

painter->DrawText(mText, mTextRendArea, &mEffects);

}

if (pLayout){

pLayout->OnRendWindow(painter);

}

//+-----------------------------

//

// 渲染子窗口

//

//+-----------------------------

if (!mChildList.empty()){

UpdateChildWindowPos();

for (auto& window : mChildList){

CDxWidget*& windowref = window.ref();

if (windowref->GetHwnd() == nullptr){

windowref->OnRendWindow(painter);

}

}

}

if (bIsNeedBorder){

RECT rc = mRendArea;

rc.left += 1;

rc.right -= 1;

rc.top += 1;

rc.bottom -= 1;

if (mEffects.GetEffectType() == CDxEffects::Dx_ImageType){

painter->DrawRectangle(rc, mBorderColor, 1);

}

else{

DXShape shape = GetWindowShape();

switch (shape)

{

case DxUI::Dx_Rectangle:

painter->DrawRectangle(rc, mBorderColor, 1);

break;

case DxUI::Dx_RoundedRectangle:

painter->DrawRoundedRectangle(rc, mRoundRectSize, mBorderColor, 1);

break;

case DxUI::Dx_Ellipse:

painter->DrawEllipse(rc, mRoundRectSize, mBorderColor, 1);

break;

default:

break;

}

}

}

if (!bIsEnabel){

DXShape shape = GetWindowShape();

mEffects.SetCurrentStatus(Dx_Disable);

mEffects.SetDisabelColor(mDisabelColor);

switch (shape)

{

case DxUI::Dx_Rectangle:

painter->FillRectangle(mRendArea, &mEffects);

break;

case DxUI::Dx_RoundedRectangle:

painter->FillRoundedRectangle(mRendArea, mRoundRectSize, &mEffects);

break;

case DxUI::Dx_Ellipse:

painter->FillEllipse(mRendArea, mRoundRectSize, &mEffects);

break;

default:

break;

}

}

}

//+----------------------------------

顯然對(duì)CDxRendImpl::OnRender2D()進(jìn)行完善:

//+---------------------------------

void CDxRendImpl::OnRender2D(){

CDxWidget* window = dynamic_cast(this);

if (window->IsNeedRender() == false)

return;

if (pRendTarget && window){

pRendTarget->BeginDraw();

pRendTarget->Clear(ToD2DColor(window->GetBackGroundColor()));

window->OnRendWindow(pPainter);

if(window->GetWindowSelfDesc() == Dx_PopWindow)

pPainter->DrawRoundedRectangle(window->GetFrameRect(), window->GetRoundRectSize(), RgbI(128, 128, 128), 2);

HRESULT hr = pRendTarget->EndDraw();

if (FAILED(hr)){

DxTRACE(L"渲染出錯(cuò)[%1]\n", hr);

}

}

else{

if (window->GetWindowSelfDesc() == Dx_Layout){

if (window->GetParent()){

window->GetParent()->OnRender2D();

return;

}

}

else if (window && window->IsVisible() ){

if (window->GetOpacity() < 0.99999999){

if (window->GetParent()){

window->GetParent()->OnRender2D();

return;

}

}

DxColor col = window->GetEraseColor();

CDxWidget* parent = window->GetParent();

if (col.rgb.a == 0 || (parent && parent->HasFloatWindow())){

if (parent){

parent->OnRender2D();

return;

}

}

auto render = GetRenderTarget();

if (render){

CDxPainter* painter = nullptr;

if (g_PainterMap.count(render)){

painter = g_PainterMap.at(render);

}

else{

painter = new CDxPainter(render);

g_PainterMap[render] = painter;

}

render->BeginDraw();

ID2D1Layer* p_ClipLayout{ nullptr };

ID2D1Geometry* p_ClipGeometry{ nullptr };

safe_release(p_ClipGeometry);

safe_release(p_ClipLayout);

render->CreateLayer(&p_ClipLayout);

RECT rc = window->GetInvalidateRect();

p_ClipGeometry = CDxResource::CreateRectGeometry(rc);

if (p_ClipLayout == nullptr || p_ClipGeometry == nullptr)

return;

render->PushLayer(D2D1::LayerParameters(

D2D1::InfiniteRect(),

p_ClipGeometry,

D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,

D2D1::IdentityMatrix(),

1.0f,

NULL,

D2D1_LAYER_OPTIONS_NONE

), p_ClipLayout);

render->Clear(ToD2DColor(col));

window->OnRendWindow(painter);

render->PopLayer();

safe_release(p_ClipGeometry);

safe_release(p_ClipLayout);

HRESULT hr = render->EndDraw();

if (FAILED(hr)){

DxTRACE(L"渲染出錯(cuò)[%1]\n", hr);

}

}

}

}

}

//+---------------------------------

我們現(xiàn)在我們可以通過子類化CDxWidget實(shí)現(xiàn)各種控件癞尚,實(shí)現(xiàn)不同的效果,最終我們可以很簡(jiǎn)單的編寫出各種樣式的界面乱陡。

這一章的重點(diǎn)并非是在這個(gè)GUI框架的實(shí)現(xiàn)上浇揩,而是主要讓我們對(duì)class,繼承和多態(tài)的使用有進(jìn)一步的理解憨颠,以及對(duì)純虛類的引入胳徽,當(dāng)然至于對(duì)該框架感興趣的同學(xué),我們會(huì)在后續(xù)的內(nèi)容里面一點(diǎn)點(diǎn)的引入爽彤,畢竟里面有很多模板的東西养盗,現(xiàn)在一下子拿出來很多東西可能會(huì)比較吃力,所以這一章的內(nèi)容還是在對(duì)C++程序設(shè)計(jì)上的一些理解适篙,怎么組合class往核,怎么使用繼承,怎么使用多態(tài)嚷节,怎么使用純虛類等聂儒,這些其實(shí)是仁者見仁智者見智的問題,但在自己沒有比較好的想法的時(shí)候有個(gè)參考總歸是好的硫痰。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末薄货,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碍论,更是在濱河造成了極大的恐慌,老刑警劉巖柄慰,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳍悠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡坐搔,警方通過查閱死者的電腦和手機(jī)藏研,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來概行,“玉大人蠢挡,你說我怎么就攤上這事。” “怎么了业踏?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵禽炬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我勤家,道長(zhǎng)腹尖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任伐脖,我火速辦了婚禮热幔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘讼庇。我一直安慰自己绎巨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布蠕啄。 她就那樣靜靜地躺著场勤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪介汹。 梳的紋絲不亂的頭發(fā)上却嗡,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音嘹承,去河邊找鬼窗价。 笑死,一個(gè)胖子當(dāng)著我的面吹牛叹卷,可吹牛的內(nèi)容都是我干的撼港。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼骤竹,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼帝牡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蒙揣,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤靶溜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后懒震,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罩息,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年个扰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓷炮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡递宅,死狀恐怖娘香,靈堂內(nèi)的尸體忽然破棺而出苍狰,到底是詐尸還是另有隱情,我是刑警寧澤烘绽,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布淋昭,位于F島的核電站,受9級(jí)特大地震影響诀姚,放射性物質(zhì)發(fā)生泄漏响牛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一赫段、第九天 我趴在偏房一處隱蔽的房頂上張望呀打。 院中可真熱鬧,春花似錦糯笙、人聲如沸贬丛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豺憔。三九已至,卻和暖如春够庙,著一層夾襖步出監(jiān)牢的瞬間恭应,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工耘眨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昼榛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓剔难,卻偏偏與公主長(zhǎng)得像胆屿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子偶宫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容