当前位置: 首页 > news >正文

【MFC】12.双缓冲序列化机制-笔记

双缓冲

双缓冲在之前写字符雨的时候,已经简单介绍过,今天我们来写一个简单的程序来体会双缓冲机制

我们实现一个在屏幕上画直线的功能:

在类中添加变量,保存起点坐标和终点坐标:

//定义一个容器,保存每次画的直线
using Lint = std::pair(CPoint,CPoint);
CList<>m_List;CPoint m_begin;
CPoint m_end;

在对话框上添加WM_MOUSEMOVE,WM_LBUTTONDOWM,WM_LBUTTONUP消息处理函数:

void C双缓冲View::OnDraw(UINT nFlags,CPoint point){C双缓冲View* pDoc = GetDocument();ASSERT_CALID(pDoc);if(!pFoc)return;//画直线pDC->MoveTo(m_begin);pDC->LineTo(m_end);CVIew::OnMouseMove(nFlags,point);
}
void C双缓冲::OnMouseMove(UINT nFlags,CPoint point){if(nFlags&MK_LBUTTON){m_end = point;IncalidateRect(NULL,TRUE);}CView::OnMouseMoce(nFlags,point);
}
void C双缓冲View::OnLButtonDown(UINT nFlags,CPoint point){//记录开始坐标CVIew::OnLButtonDown(nFlags,point);m_begin = point;
}
void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){//记录终点坐标m_end = point;CVIew::OnLButtonUp(nFlags,point);ReleaseCapture();
}

这样写完之后,没有反应,这是因为没有无效区域,我们将OnLButtonUp函数中添加无效区域就可以了:

void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){//记录终点坐标m_end = point;CVIew::OnLButtonUp(nFlags,point);InvalidateRect(NULL,TRUE);
}

只有有了无效区域,绘图消息才会产生

然后我们完善:

//定义一个容器,保存每次画的直线
using Lint = std::pair(CPoint,CPoint);
CList<>m_List;
void C双缓冲View::OnDraw(UINT nFlags,CPoint point){C双缓冲View* pDoc = GetDocument();ASSERT_CALID(pDoc);if(!pFoc)return;pDC->MoveTo(m_begin);pDC->LineTo(m_end);auto pos = m_list.GetHeadPossition();while(pos){auto Lint = m_list.GetNext(pos);//画直线pDC->MoveTo(Line.first);pDC->LineTo(line.second);}CVIew::OnMouseMove(nFlags,point);
}
void C双缓冲::OnMouseMove(UINT nFlags,CPoint point){if(nFlags&MK_LBUTTON){m_end = point;IncalidateRect(NULL,TRUE);}CView::OnMouseMoce(nFlags,point);
}
void C双缓冲View::OnLButtonDown(UINT nFlags,CPoint point){//记录开始坐标CVIew::OnLButtonDown(nFlags,point);m_begin = point;//捕捉客户区外鼠标消息SetCapture();
}
void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){//记录终点坐标m_end = point;m_list.AddTail(Line(m_begin,m_end));CVIew::OnLButtonUp(nFlags,point);InvalidateRect(NULL,TRUE);ReleaseCapture();
}

这样写完了之后,确实可以画出来直线,但是这是我们直接操作外设的,所以会出现闪屏的情况

这时候就需要我们的双缓冲了

双缓冲就是我们操作内存,将直线画在内存上,然后将内存完整拷贝到外设上,这样就可以避免操作慢,闪屏的问题:

Win32中,能操作设备的,只有设备句柄hdc

而在MFC中封装了:

CDC------->对应Win32::GetDC

CMetaFileDC 矢量图,位图(了解一下就行了)

CPainDC:WM_PAINT::BEGINPAINT

CWindowDC:桌面窗口的HDC


这几种对象,能代表他们各自的东西,肯定是内部有绑定机制Attah()

双缓冲:内存DC,这里的CDC就代表了内存DC

然后我们修改代码:

void C双缓冲View::OnDraw(UINT nFlags,CPoint point){C双缓冲View* pDoc = GetDocument();ASSERT_CALID(pDoc);if(!pFoc)return;//双缓冲绘图//1.创建内存DCCDC dcMem;dcMem.CreateCompatibleDC(pDC);CRect rect;GetClintRect(rect);//2.创建一张屏幕DC一样的位图CBitmap bitmap;bitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//3.送到内存DC中dcMem.SeleObject(bitmap);dcMem.FillSolidRect(rect,RGB(255,255,255));//然后我们使用内存DC绘图dcMem->MoveTo(m_begin);dcMem->LineTo(m_end);auto pos = m_list.GetHeadPossition();while(pos){auto Lint = m_list.GetNext(pos);//画直线dcMem->MoveTo(Line.first);dcMem->LineTo(line.second);}//4.拷贝到设备pDC.BitBit(0,0,rect.Width(),rect.Height(),&dcMem,0,DRCCOPY);CVIew::OnMouseMove(nFlags,point);
}

修改完这个函数后,将绘制无效区域的函数,不再擦除背景

  • 画壁画刷位图

    在Win32中都是GDI句柄

    MFC封装了GDI对象

    Win32什么流程

    MFC就还是什么流程

    void C双缓冲View::OnDraw(UINT nFlags,CPoint point){C双缓冲View* pDoc = GetDocument();ASSERT_CALID(pDoc);if(!pFoc)return;//双缓冲绘图//1.创建内存DCCDC dcMem;dcMem.CreateCompatibleDC(pDC);CRect rect;GetClintRect(rect);//2.创建一张屏幕DC一样的位图CBitmap bitmap;bitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());CPen pen;pen.CreatePen(PS_DOT,55,RGB(0,255,255));//把画笔送到内存DCdeMem.SelectObject(pen);//3.送到内存DC中dcMem.SeleObject(bitmap);dcMem.FillSolidRect(rect,RGB(255,255,255));//然后我们使用内存DC绘图dcMem->MoveTo(m_begin);dcMem->LineTo(m_end);auto pos = m_list.GetHeadPossition();while(pos){auto Lint = m_list.GetNext(pos);//画直线dcMem->MoveTo(Line.first);dcMem->LineTo(line.second);}//4.拷贝到设备pDC.BitBit(0,0,rect.Width(),rect.Height(),&dcMem,0,DRCCOPY);CVIew::OnMouseMove(nFlags,point);
    }
    

序列化

  • 为什么要有序列化:

    我们在绘图应用程序上绘制的图形,可以保存起来,我们之后还可以打开

    而我们上面写的程序,是不能保存的,这就是序列化的基本功能

  • 简单使用以下序列化:

    1. 首先继承于CObject
    2. 类内添加声明宏DECLARE_SERTAL
    3. 类外实现宏IMPLEMENT_SERIAL
    //直线类
    class Cline:public Cobject{
    public:CLine(){};CLine(int x,int y,CString type):x(x),y(y),str(type){};virtual coid Setialize(CArchice* ar){if(ar,IsLoading()){//加载ar>>x>>y>>str;}else{//保存ar<<x<<y<<str;}}int x;int y;int color;CString str;DECLARE_SERTAL(CLine)
    }
    IMPLEMENT_SERIAL(CLine,Cobject,1)
    
    int main(){int nRetCode = 0;HMODULE hModule = ::GEtModuleHandle(nullptr);AfxWinInit(hModule,nullptr,::GetCommandLine(),0);CLine line(40,40,"直线");CFile file;file.Open(R"(文件路径\文件名)",CFile::modeCreate|CFile::modeWrite);CArchive ar(&file,CArchice::store,4096);ar<<&line;ar.Close();file.Close();return nRetCode;
    }
    

我们这样实现后,发现,我们只写了三个成员,但是文件中有很多,我们来简单追踪有一下序列化是如何实现的:

序列化过程
CArchive 归档类对象

CArchive& AFXAPI operator>>(CArchive& ar, CLine* &pOb)
{pOb = (CLine*) ar.ReadObject(RUNTIME_CLASS(CLine)); return ar; 
}CArchive ar(&file, CArchive::store,4096)
{m_nMode = nMode;//模式m_pFile = pFile;//文件句柄m_nBufSize = nBufSize	//缓冲区大小this->m_lpBufStart = new BYTE[m_nBufSize]; //申请了缓冲区大小m_lpBufMax = m_lpBufStart + nBufSize;//缓冲区的尾地址m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;
}struct 
{类版本类大小x
}void CArchive::WriteObject(const CObject* line)
{//获取类信息地址CRuntimeClass* pClassRef = pOb->GetRuntimeClass();//写类信息WriteClass(pClassRef);((CObject*)pOb)->Serialize(ar对象/*归档类对象*/);{ar << x{return CArchive::operator<<((char)x); {if (m_lpBufCur + sizeof(LONG) > m_lpBufMax) Flush();*(UNALIGNED LONG*)m_lpBufCur = x;m_lpBufCur += sizeof(LONG);return *this; }}AfxWriteStringLength(*this, 长度, 判断字符串是否是多字节{ar<<(BYTE)nLength;}:memcpy_s(m_lpBufCur, (size_t)(m_lpBufMax - m_lpBufCur), lpBuf, nTemp)}
}ar,close
{Flush();{	m_pFile->Write(m_lpBufStart, ULONG(m_lpBufCur - m_lpBufStart){//写文件::WriteFile(m_hFile, lpBuf, nCount, &nWritten, NULL)m_lpBufCur = m_lpBufStart;}}
}

反序列化:

int main(){int nRetCode = 0;HMODULE hModule = ::GEtModuleHandle(nullptr);AfxWinInit(hModule,nullptr,::GetCommandLine(),0);CLine* line;CFile file;file.Open(R"(文件路径\文件名)",CFile::modeCreate|CFile::modeRead);CArchive ar(&file,CArchice::store,4096);ar >> line;ar.Close();file.Close();std::cout<<line->str;std::coutd<<line-x;std::cout<<line->y;return nRetCode;
}
CArchive ar(&file, CArchive::store,4096)
{m_nMode = nMode;//模式 读m_pFile = pFile;//文件句柄m_nBufSize = nBufSize	//缓冲区大小this->m_lpBufStart = new BYTE[m_nBufSize]; //申请了缓冲区大小m_lpBufMax = m_lpBufStart + nBufSize;//缓冲区的尾地址m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;
}ar.ReadObject(直线类信息)
{//读类信息CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag);{BYTE* lpTemp = m_lpBufStart + nPreviouslyFilled;do{//读文件 全部读上来nBytesRead = m_pFile->Read(lpTemp, nLeftToRead);lpTemp = lpTemp + nBytesRead;nTotalInBuffer += nBytesRead;nLeftToRead -= nBytesRead;}while (nBytesRead > 0 && nLeftToRead > 0 && nTotalInBuffer < nTotalSizeWanted);m_lpBufCur = m_lpBufStart;m_lpBufMax = m_lpBufStart + nTotalInBuffer;}//动态创建对象pOb = pClassRef->CreateObject();//回到我们代码pOb->Serialize(*this){CArchive::operator>>((LONG&)x);{if (m_lpBufCur + sizeof(LONG) > m_lpBufMax)FillBuffer(UINT(sizeof(LONG) - (m_lpBufMax - m_lpBufCur)));i = *(UNALIGNED LONG*)m_lpBufCur;m_lpBufCur += sizeof(LONG);return *this; }}
}

了解了序列化和反序列化过程,我们就可以将我们画的图保存起来了:

  1. 添加一个直线类
    #pragma once
    #include <afx.h>
    class Cline:public CObject{
    public:DECLARE_SERTAL(CLine)virtual void Serialize(CArchive& ar);CPoint m_begin;CPoint m_end;
    };//在cpp中实现:
    #include "pch.h"
    #include "CLine.h"IMPLEMENT_SERIAL(CLine,CObject,1)void Serialize(CArchive& ar){CObject::Serialize(ae);if(ar.IsLoading()){ar>>m_begin.x>>m_begin.y;ar>>m_end.x>>m_end.y;}else{ar<<m_begin.x<<m_begin.y;ar<<m_end.x<<m_end.y;}
    }我们还需要将直线的链表存储到文档中:
    在文档类.cpp中:
    #include "CLine.h"为文档类添加变量:
    CList<CLine>m_list;
    
  2. 我们将OnDraw消息稍作修改:就是直线的数据获取位置不一样了,我们需要修改:
  auto pos = pDoc->m_list.GetHeadPossition();while(pos){auto Lint = pDoc->m_list.GetNext(pos);//画直线dcMem->MoveTo(Line.m_begin);dcMem->LineTo(line.m_end);}
  1. 这样修改之后,我们还需要将直线类的拷贝构造函数修改,因为默认是浅拷贝,我们在OnDraw函数中使用的时候,显示已经释放:
#pragma once
#include <afx.h>
class Cline:public CObject{
public:CLine(){};CLine& operator_(const& line){this->m_begin = line.m_begin;this->m_end = list.m_red;return *this;}CLine(const CLine& line){this->m_begin = line.m_begin;this->m_end = list.m_red;}CLine(CPoint begin,CPoint end){this->m_begin = begin;this->m_end = end;}DECLARE_SERTAL(CLine)virtual void Serialize(CArchive& ar);CPoint m_begin;CPoint m_end;
};
  1. 然后我们去文档类中处理:
void C...DOC::Serialize(CArchive& ar){if(ar.IsStoring()){ar<<m_list.getSize();auto pos = m_list.GetHeadPosition();while(pos){//ar<<&m_list.GetNext(pos);CLine*p = m_listl.GetNext(pos);ar<<&p;//这里第二种方式,看似跟上面的方式没有什么区别,但是运行发现,只保存了一条直线//这是因为每一次p都是一个地址}else{int nSize;ar>>nSize;CObject* shape;while(nSize--){ar>>shape;m_list.AddTail(*(CLine*)shape);}}}
}

双缓冲

双缓冲在之前写字符雨的时候,已经简单介绍过,今天我们来写一个简单的程序来体会双缓冲机制

我们实现一个在屏幕上画直线的功能:

在类中添加变量,保存起点坐标和终点坐标:

//定义一个容器,保存每次画的直线
using Lint = std::pair(CPoint,CPoint);
CList<>m_List;CPoint m_begin;
CPoint m_end;

在对话框上添加WM_MOUSEMOVE,WM_LBUTTONDOWM,WM_LBUTTONUP消息处理函数:

void C双缓冲View::OnDraw(UINT nFlags,CPoint point){C双缓冲View* pDoc = GetDocument();ASSERT_CALID(pDoc);if(!pFoc)return;//画直线pDC->MoveTo(m_begin);pDC->LineTo(m_end);CVIew::OnMouseMove(nFlags,point);
}
void C双缓冲::OnMouseMove(UINT nFlags,CPoint point){if(nFlags&MK_LBUTTON){m_end = point;IncalidateRect(NULL,TRUE);}CView::OnMouseMoce(nFlags,point);
}
void C双缓冲View::OnLButtonDown(UINT nFlags,CPoint point){//记录开始坐标CVIew::OnLButtonDown(nFlags,point);m_begin = point;
}
void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){//记录终点坐标m_end = point;CVIew::OnLButtonUp(nFlags,point);ReleaseCapture();
}

这样写完之后,没有反应,这是因为没有无效区域,我们将OnLButtonUp函数中添加无效区域就可以了:

void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){//记录终点坐标m_end = point;CVIew::OnLButtonUp(nFlags,point);InvalidateRect(NULL,TRUE);
}

只有有了无效区域,绘图消息才会产生

然后我们完善:

//定义一个容器,保存每次画的直线
using Lint = std::pair(CPoint,CPoint);
CList<>m_List;
void C双缓冲View::OnDraw(UINT nFlags,CPoint point){C双缓冲View* pDoc = GetDocument();ASSERT_CALID(pDoc);if(!pFoc)return;pDC->MoveTo(m_begin);pDC->LineTo(m_end);auto pos = m_list.GetHeadPossition();while(pos){auto Lint = m_list.GetNext(pos);//画直线pDC->MoveTo(Line.first);pDC->LineTo(line.second);}CVIew::OnMouseMove(nFlags,point);
}
void C双缓冲::OnMouseMove(UINT nFlags,CPoint point){if(nFlags&MK_LBUTTON){m_end = point;IncalidateRect(NULL,TRUE);}CView::OnMouseMoce(nFlags,point);
}
void C双缓冲View::OnLButtonDown(UINT nFlags,CPoint point){//记录开始坐标CVIew::OnLButtonDown(nFlags,point);m_begin = point;//捕捉客户区外鼠标消息SetCapture();
}
void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){//记录终点坐标m_end = point;m_list.AddTail(Line(m_begin,m_end));CVIew::OnLButtonUp(nFlags,point);InvalidateRect(NULL,TRUE);ReleaseCapture();
}

这样写完了之后,确实可以画出来直线,但是这是我们直接操作外设的,所以会出现闪屏的情况

这时候就需要我们的双缓冲了

双缓冲就是我们操作内存,将直线画在内存上,然后将内存完整拷贝到外设上,这样就可以避免操作慢,闪屏的问题:

Win32中,能操作设备的,只有设备句柄hdc

而在MFC中封装了:

CDC------->对应Win32::GetDC

CMetaFileDC 矢量图,位图(了解一下就行了)

CPainDC:WM_PAINT::BEGINPAINT

CWindowDC:桌面窗口的HDC


这几种对象,能代表他们各自的东西,肯定是内部有绑定机制Attah()

双缓冲:内存DC,这里的CDC就代表了内存DC

然后我们修改代码:

void C双缓冲View::OnDraw(UINT nFlags,CPoint point){C双缓冲View* pDoc = GetDocument();ASSERT_CALID(pDoc);if(!pFoc)return;//双缓冲绘图//1.创建内存DCCDC dcMem;dcMem.CreateCompatibleDC(pDC);CRect rect;GetClintRect(rect);//2.创建一张屏幕DC一样的位图CBitmap bitmap;bitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//3.送到内存DC中dcMem.SeleObject(bitmap);dcMem.FillSolidRect(rect,RGB(255,255,255));//然后我们使用内存DC绘图dcMem->MoveTo(m_begin);dcMem->LineTo(m_end);auto pos = m_list.GetHeadPossition();while(pos){auto Lint = m_list.GetNext(pos);//画直线dcMem->MoveTo(Line.first);dcMem->LineTo(line.second);}//4.拷贝到设备pDC.BitBit(0,0,rect.Width(),rect.Height(),&dcMem,0,DRCCOPY);CVIew::OnMouseMove(nFlags,point);
}

修改完这个函数后,将绘制无效区域的函数,不再擦除背景

  • 画壁画刷位图

    在Win32中都是GDI句柄

    MFC封装了GDI对象

    Win32什么流程

    MFC就还是什么流程

    void C双缓冲View::OnDraw(UINT nFlags,CPoint point){C双缓冲View* pDoc = GetDocument();ASSERT_CALID(pDoc);if(!pFoc)return;//双缓冲绘图//1.创建内存DCCDC dcMem;dcMem.CreateCompatibleDC(pDC);CRect rect;GetClintRect(rect);//2.创建一张屏幕DC一样的位图CBitmap bitmap;bitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());CPen pen;pen.CreatePen(PS_DOT,55,RGB(0,255,255));//把画笔送到内存DCdeMem.SelectObject(pen);//3.送到内存DC中dcMem.SeleObject(bitmap);dcMem.FillSolidRect(rect,RGB(255,255,255));//然后我们使用内存DC绘图dcMem->MoveTo(m_begin);dcMem->LineTo(m_end);auto pos = m_list.GetHeadPossition();while(pos){auto Lint = m_list.GetNext(pos);//画直线dcMem->MoveTo(Line.first);dcMem->LineTo(line.second);}//4.拷贝到设备pDC.BitBit(0,0,rect.Width(),rect.Height(),&dcMem,0,DRCCOPY);CVIew::OnMouseMove(nFlags,point);
    }
    

序列化

  • 为什么要有序列化:

    我们在绘图应用程序上绘制的图形,可以保存起来,我们之后还可以打开

    而我们上面写的程序,是不能保存的,这就是序列化的基本功能

  • 简单使用以下序列化:

    1. 首先继承于CObject
    2. 类内添加声明宏DECLARE_SERTAL
    3. 类外实现宏IMPLEMENT_SERIAL

    ```cpp //直线类 class Cline:public Cobject{ public: CLine(){}; CLine(int x,int y,CString type):x(x),y(y),str(type){}; virtual coid Setialize(CArchice* ar){ if(ar,IsLoading()){ //加载 ar>>x>>y>>str; }else{ //保存 ar<

我们这样实现后,发现,我们只写了三个成员,但是文件中有很多,我们来简单追踪有一下序列化是如何实现的:

序列化过程
CArchive 归档类对象

CArchive& AFXAPI operator>>(CArchive& ar, CLine* &pOb)
{pOb = (CLine*) ar.ReadObject(RUNTIME_CLASS(CLine)); return ar; 
}CArchive ar(&file, CArchive::store,4096)
{m_nMode = nMode;//模式m_pFile = pFile;//文件句柄m_nBufSize = nBufSize	//缓冲区大小this->m_lpBufStart = new BYTE[m_nBufSize]; //申请了缓冲区大小m_lpBufMax = m_lpBufStart + nBufSize;//缓冲区的尾地址m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;
}struct 
{类版本类大小x
}void CArchive::WriteObject(const CObject* line)
{//获取类信息地址CRuntimeClass* pClassRef = pOb->GetRuntimeClass();//写类信息WriteClass(pClassRef);((CObject*)pOb)->Serialize(ar对象/*归档类对象*/);{ar << x{return CArchive::operator<<((char)x); {if (m_lpBufCur + sizeof(LONG) > m_lpBufMax) Flush();*(UNALIGNED LONG*)m_lpBufCur = x;m_lpBufCur += sizeof(LONG);return *this; }}AfxWriteStringLength(*this, 长度, 判断字符串是否是多字节{ar<<(BYTE)nLength;}:memcpy_s(m_lpBufCur, (size_t)(m_lpBufMax - m_lpBufCur), lpBuf, nTemp)}
}ar,close
{Flush();{	m_pFile->Write(m_lpBufStart, ULONG(m_lpBufCur - m_lpBufStart){//写文件::WriteFile(m_hFile, lpBuf, nCount, &nWritten, NULL)m_lpBufCur = m_lpBufStart;}}
}

反序列化:

int main(){int nRetCode = 0;HMODULE hModule = ::GEtModuleHandle(nullptr);AfxWinInit(hModule,nullptr,::GetCommandLine(),0);CLine* line;CFile file;file.Open(R"(文件路径\文件名)",CFile::modeCreate|CFile::modeRead);CArchive ar(&file,CArchice::store,4096);ar >> line;ar.Close();file.Close();std::cout<<line->str;std::coutd<<line-x;std::cout<<line->y;return nRetCode;
}
CArchive ar(&file, CArchive::store,4096)
{m_nMode = nMode;//模式 读m_pFile = pFile;//文件句柄m_nBufSize = nBufSize	//缓冲区大小this->m_lpBufStart = new BYTE[m_nBufSize]; //申请了缓冲区大小m_lpBufMax = m_lpBufStart + nBufSize;//缓冲区的尾地址m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;
}ar.ReadObject(直线类信息)
{//读类信息CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag);{BYTE* lpTemp = m_lpBufStart + nPreviouslyFilled;do{//读文件 全部读上来nBytesRead = m_pFile->Read(lpTemp, nLeftToRead);lpTemp = lpTemp + nBytesRead;nTotalInBuffer += nBytesRead;nLeftToRead -= nBytesRead;}while (nBytesRead > 0 && nLeftToRead > 0 && nTotalInBuffer < nTotalSizeWanted);m_lpBufCur = m_lpBufStart;m_lpBufMax = m_lpBufStart + nTotalInBuffer;}//动态创建对象pOb = pClassRef->CreateObject();//回到我们代码pOb->Serialize(*this){CArchive::operator>>((LONG&)x);{if (m_lpBufCur + sizeof(LONG) > m_lpBufMax)FillBuffer(UINT(sizeof(LONG) - (m_lpBufMax - m_lpBufCur)));i = *(UNALIGNED LONG*)m_lpBufCur;m_lpBufCur += sizeof(LONG);return *this; }}
}

了解了序列化和反序列化过程,我们就可以将我们画的图保存起来了:

  1. 添加一个直线类
    #pragma once
    #include <afx.h>
    class Cline:public CObject{
    public:DECLARE_SERTAL(CLine)virtual void Serialize(CArchive& ar);CPoint m_begin;CPoint m_end;
    };//在cpp中实现:
    #include "pch.h"
    #include "CLine.h"IMPLEMENT_SERIAL(CLine,CObject,1)void Serialize(CArchive& ar){CObject::Serialize(ae);if(ar.IsLoading()){ar>>m_begin.x>>m_begin.y;ar>>m_end.x>>m_end.y;}else{ar<<m_begin.x<<m_begin.y;ar<<m_end.x<<m_end.y;}
    }我们还需要将直线的链表存储到文档中:
    在文档类.cpp中:
    #include "CLine.h"为文档类添加变量:
    CList<CLine>m_list;
    
  2. 我们将OnDraw消息稍作修改:就是直线的数据获取位置不一样了,我们需要修改:
  auto pos = pDoc->m_list.GetHeadPossition();while(pos){auto Lint = pDoc->m_list.GetNext(pos);//画直线dcMem->MoveTo(Line.m_begin);dcMem->LineTo(line.m_end);}
  1. 这样修改之后,我们还需要将直线类的拷贝构造函数修改,因为默认是浅拷贝,我们在OnDraw函数中使用的时候,显示已经释放:
#pragma once
#include <afx.h>
class Cline:public CObject{
public:CLine(){};CLine& operator_(const& line){this->m_begin = line.m_begin;this->m_end = list.m_red;return *this;}CLine(const CLine& line){this->m_begin = line.m_begin;this->m_end = list.m_red;}CLine(CPoint begin,CPoint end){this->m_begin = begin;this->m_end = end;}DECLARE_SERTAL(CLine)virtual void Serialize(CArchive& ar);CPoint m_begin;CPoint m_end;
};
  1. 然后我们去文档类中处理:
void C...DOC::Serialize(CArchive& ar){if(ar.IsStoring()){ar<<m_list.getSize();auto pos = m_list.GetHeadPosition();while(pos){//ar<<&m_list.GetNext(pos);CLine*p = m_listl.GetNext(pos);ar<<&p;//这里第二种方式,看似跟上面的方式没有什么区别,但是运行发现,只保存了一条直线//这是因为每一次p都是一个地址}else{int nSize;ar>>nSize;CObject* shape;while(nSize--){ar>>shape;m_list.AddTail(*(CLine*)shape);}}}
}

相关文章:

【MFC】12.双缓冲序列化机制-笔记

双缓冲 双缓冲在之前写字符雨的时候&#xff0c;已经简单介绍过&#xff0c;今天我们来写一个简单的程序来体会双缓冲机制 我们实现一个在屏幕上画直线的功能&#xff1a; 在类中添加变量&#xff0c;保存起点坐标和终点坐标&#xff1a; //定义一个容器&#xff0c;保存每…...

Linux 终端会话中,启动任务并放到后台运行

一、需求 linux要执行一个脚本&#xff0c;耗时很长&#xff0c;想要脚本在后台运行&#xff0c;用户注销或终端软件关闭时也可以继续运行。 二、实现 1、nohup命令 脚本在后台运行 nohup 是在 Linux 和类 Unix 系统中使用的一个命令&#xff0c;用于在后台运行程序&#x…...

软考笔记——10.项目管理

进度管理 进度管理就是采用科学的方法&#xff0c;确定进度目标&#xff0c;编制进度计划和资源供应计划&#xff0c;进行进度控制&#xff0c;在与质量、成本目标协调的基础上&#xff0c;实现工期目标。 具体来说&#xff0c;包括以下过程&#xff1a; (1) 活动定义&#…...

算法与数据结构(二十四)最优子结构原理和 dp 数组遍历方向

注&#xff1a;此文只在个人总结 labuladong 动态规划框架&#xff0c;仅限于学习交流&#xff0c;版权归原作者所有&#xff1b; 本文是两年前发的 动态规划答疑篇open in new window 的修订版&#xff0c;根据我的不断学习总结以及读者的评论反馈&#xff0c;我给扩展了更多…...

Java Vue Uniapp MES生产执行管理系统

本MES系统是一款B/S结构、通用的生产执行管理系统&#xff0c;功能强大&#xff01; 系统基于多年离散智造行业的业务经验组建&#xff0c;主要目的是为国内离散制造业的中小企业提供一个专业化、通用性、低成本的MES系统解决方案。 联系作者获取...

深入探究Socks5代理与IP代理在网络安全与爬虫中的应用

1. Socks5代理&#xff1a;打开网络隧道的多功能工具 Socks5代理是一种流行的代理协议&#xff0c;它在传输层为数据包提供了隧道。相较于之前的版本&#xff0c;Socks5不仅支持TCP连接&#xff0c;还可以处理UDP流量&#xff0c;使其在需要实时数据传输的应用中表现出色。在网…...

Vue使用jspdf和html2canvas组件库结合导出PDF文件

效果图&#xff1a; 1、安装依赖&#xff1a; npm install html2canvas --save npm install jspdf --save 或 yarn add html2canvas --save yarn add jspdf --save 2、封装全局调用方法&#xff1a;this.$exportPDF(#id,文件名) 新建js文件&#xff1a;/utils/html2Pdf.js&am…...

7. 实现 API 自动生成

目录 1. pom.xml中引用依赖 2. 引入相关的依赖 3. 编写配置类 4. application.yml 中添加配置 5. API 常用注解 6. 访问 API 列表 7. API 导入 Postman 使用 Springfox Swagger生成 API&#xff0c;并导入 Postman&#xff0c;完成API单元测试。 Swagger 简介&#xff1a;Swag…...

使用Druid解析SQL,获取SQL中所有使用的表

一、sqlParse组成 Druid SQL Parser分三个模块&#xff1a; - Parser - AST - Visitor 1.1 Parser parser是将输入文本转换为ast&#xff08;抽象语法树&#xff09;&#xff0c;parser有包括两个部分&#xff0c;Parser和Lexer&#xff0c;其中Lexer实现词法分析&#x…...

公司内部测试团队可以替代专业的软件检测机构吗,性能测试怎么收费?

第三方软件测试 尽管软件测试是伴随着软件开发的发展而产生的&#xff0c;但是在信息技术日新月异的今天&#xff0c;软件测试逐渐走出开发附庸的定位。 一方面&#xff0c;很多大型企业都在内部设置了专门的测试团队以承接软件系统的测试工作&#xff0c;为产品质量把关。另…...

Three.js之相机、渲染器、光源、动画、性能监测

参考资料 第一个3D案例—透视投影相机第一个3D案例—渲染器…Canvas画布布局和全屏 知识点 透视投影相机PerspectiveCameraWebGL渲染器WebGLRenderer辅助观察坐标系AxesHelper漫反射网格材质MeshLambertMaterial点光源PointLight点光源辅助观察PointLightHelper环境光Ambien…...

Seaborn图表使用指南!

目录 介绍线图散点图直方图概率密度函数 &#xff08;PDF&#xff09;箱线图小提琴剧情配对图热图关节图地毯图 一、介绍 数据科学已成为一个突出的领域&#xff0c;近年来呈爆炸性增长。对精通从数据中获取见解并应用这些见解来解决现实世界问题的数据科学家的需求从未增加。…...

[C++ 网络协议编程] TCP/IP协议

目录 1. TCP/IP协议栈 2. TCP原理 2.1 TCP套接字中的I/O缓冲 2.2 TCP工作原理 2.2.1 三次握手&#xff08;连接&#xff09; 2.2.2 与对方主机的数据交换 2.2.3 四次握手&#xff08;断开与套接字的连接&#xff09; TCP&#xff08;Transmission Control Protocol传输控…...

Unity用NPOI创建Exect表,保存数据,和修改删除数据。以及打包后的坑——无法打开新创建的Exect表

先说坑花了一下午才找到解决方法解决&#xff0c; 在Unity编辑模式下点击物体创建对应的表&#xff0c;获取物体名字与在InputText填写的注释数据。然后保存。创建Exect表可以打开&#xff0c;打包PC后&#xff0c;点击物体创建的表&#xff0c;打不开文件破损 解决方法&#…...

记一次fegin调用的媒体类型问题

1.问题&#xff1a;分页查询&#xff0c;分页参数传递不生效 2.开发环境&#xff1a;fegin接口 开发环境&#xff1a;调用接口 3.修改后&#xff1a;fegin接口不变 调用接口 前端媒体类型&#xff1a; 问题解决&#xff01;&#xff01;&#xff01; 4.原因分析&…...

在Hive/Spark上运行执行TPC-DS基准测试 (ORC和TEXT格式)

目前,在Hive/Spark上运行TPC-DS Benchmark主要是通过早期由Hortonworks维护的一个项目:hive-testbench 来完成的。本文我们以该项目为基础介绍一下具体的操作步骤。不过,该项目仅支持生成ORC和TEXT格式的数据,如果需要Parquet格式,请参考此文《在Hive/Spark上执行TPC-DS基…...

如何仿写简易tomcat 实现思路+代码详细讲解

仿写之前&#xff0c;我们要搞清楚都要用到哪些技术 自定义注解&#xff0c;比如Tomcat使用的是Servlet&#xff0c;我们可以定义一个自己的MyServlet构造请求体和返回体&#xff0c;比如tomcat使用HttpRequest&#xff0c;我们可以自己定义myHttpRequestjava去遍历一个指定目…...

如何提高深度学习性能

可用于 对抗过度拟合并获得更好泛化能力的20 个提示、技巧和技术 如何从深度学习模型中获得更好的性能? 这是我最常被问到的问题之一。 可能会被问为: 如何提高准确率? ……或者可以反过来说: 如果我的神经网络表现不佳该怎么办? 我经常回答说:“我不太清楚,但我有很…...

ECMAScript版本对比:从ES1到ES2021

引言 ECMAScript&#xff08;简称ES&#xff09;是一种用于编写Web前端JavaScript的标准化语言。自1997年发布第一版&#xff08;ES1&#xff09;以来&#xff0c;ECMAScript已经经历了多个版本的更新和演进。每个版本都引入了新的语法和功能&#xff0c;为开发人员提供了更强…...

设计HTML5表格

在网页设计中&#xff0c;表格主要用于显示包含行、列结构的二维数据&#xff0c;如财务表格、调查数据、日历表、时刻表、节目表等。在大多数情况下&#xff0c;这类信息都由列标题或行标题及数据构成。本章将详细介绍表格在网页设计中的应用&#xff0c;包括设计符合标准化的…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化

是不是受够了安装了oracle database之后sqlplus的简陋&#xff0c;无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话&#xff0c;配置.bahs_profile后也能解决上下翻页这些&#xff0c;但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可&#xff0c…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...