【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);}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/37217.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【189】Java Spring利用HTTP轮询远程控制树莓派4B继电器开关

因为项目需求&#xff0c;要实现PC远程控制警铃的效果。警铃结构简单&#xff0c;只需要通上12V的直流电就可以报警。本文的树莓派设备是在树莓派4B的基础上找硬件厂商搞的定制化产品。树莓派4B通过4G网卡连接互联网&#xff0c;并利用GPIO控制12V直流电的继电器开关。树莓派4B…

【设计模式】责任链模式

顾名思义&#xff0c;责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;为请求创建了一个接收者对象的链。这种模式给予请求的类型&#xff0c;对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。 在这种模式中&#xff0c;通常每个接收者…

移动端预览指定链接的pdf文件流

场景 直接展示外部系统返回的获取文件流时出现了跨域问题&#xff1a; 解决办法 1. 外部系统返回的请求头中调整&#xff08;但是其他系统不会给你改的&#xff09; 2. 我们系统后台获取文件流并转为新的文件流提供给前端 /** 获取传入url文件流 */ GetMapping("/get…

Java 正则表达式【非贪婪匹配、格式验证、反向引用、API】

非贪婪匹配 非贪婪匹配的元字符是问号 ? 当此字符跟在任何其他限定符&#xff08;*、、&#xff1f;、{n}、{m}、{n,m}&#xff09;之后&#xff0c;匹配模式是 "非贪心的"。非贪心的意思就是每次匹配搜索到的尽可能短的字符串&#xff0c;可以是0个。 案例 对…

30 | 中国高校数据分析

一、数据源 本项目使用了两个csv的数据文件,一个是中国高校(大学)的数据,一个是中国高校专业设置的数据 数据基本栏位:高校(大学)的数据高校专业设置的数据学校学校省份专业类别城市专业名称地址国家特色专业水平层次办学类别办学类型985211双一流二、数据分析目标 本…

电脑打开对话框中没有桌面这个选项解决办法

问题描述&#xff1a; 左侧栏中的桌面图标不显示 解决方法&#xff1a; 左侧的空白处右键-显示所有的文件夹 这时所有的文件夹都显示了&#xff01;

从鲁大师十五年,寻找软件的生存法则

千禧之年&#xff0c;国内互联网用户数量首次突破1000万大关&#xff0c;互联网的腾飞正式拉开序幕。 从彼时算起&#xff0c;中国互联网发展也不过23年&#xff0c;而我们记忆中那个摇着蒲扇的老头&#xff0c;却占据了其中关键的十五年。 这十五年中有太多曾经为人熟知的软件…

pointpillars怎么查看tensorboard

在PointPillars中使用TensorBoard来可视化训练过程和模型性能是很常见的做法。TensorBoard是TensorFlow提供的一个强大的工具&#xff0c;用于可视化训练过程、模型图、损失曲线、准确率等。下面是在PointPillars中使用TensorBoard的一般步骤&#xff1a; 请注意&#xff0c;上…

CHATGPT源码简介与使用指南

CHATGPT源码的基本介绍 CHATGPT源码备受关注&#xff0c;它是一款基于人工智能的聊天机器人&#xff0c;旨在帮助开发者快速搭建自己的聊天机器人&#xff0c;无需编写代码。下面是对CHATGPT搭建源码的详细介绍。 CHATGPT源码的构建和功能 CHATGPT源码是基于Google的自然语言…

flutter开发实战-MethodChannel实现flutter与iOS双向通信

flutter开发实战-MethodChannel实现flutter与iOS双向通信 最近开发中需要iOS与flutter实现通信&#xff0c;这里使用的MethodChannel 如果需要flutter与Android实现双向通信&#xff0c;请看 https://blog.csdn.net/gloryFlow/article/details/132218837 这部分与https://bl…

Linux——基础IO(1)

目录 0. 文件先前理解 1. C文件接口 1.1 写文件 1.2 读文件 1.3 输出信息到显示器 1.4 总结 and stdin & stdout & stderr 2. 系统调用文件I/O 2.1 系统接口使用示例 2.2 接口介绍 2.3 open函数返回值 3. 文件描述符fd及重定向 3.1 0 & 1 & 2 3.2…

【Spring Cloud Alibaba】RocketMQ的基础使用,如何发送消息和消费消息

在现代分布式架构的开发中&#xff0c;消息队列扮演着至关重要的角色&#xff0c;用于解耦系统组件、保障可靠性以及实现异步通信。RocketMQ作为一款开源的分布式消息中间件&#xff0c;凭借其高性能、高可用性和良好的扩展性&#xff0c;成为了众多企业在构建高可靠性、高吞吐…

运维面试大全

文章目录 第一阶段平常怎么处理故障,思路是什么样的公网和私网分类以及范围,本机地址,网络地址,广播地址交换机的工作原理ICMP是什么干什么用的,它有哪些命令TCP和UDP协议的区别tcp有哪些控制位,分别是什么意思你是用过哪些Linux命令Linux 系统安全优化与内核优化经常使用…

stable diffusion 单张图片换头roop安装配置

1.首先安装秋叶大佬的webui 2.然后在拓展里面搜索roop,下载roop插件,然后重启webui 3.重启后,在文生图和图生图的界面,就可以看到roop的入口 4.这里面,需要提前安装Visual Studio. 勾选一些必要的选项,这里可以参照b站的视频 # 秋叶版本Stablediffusion的Roop插件的安装 …

JavaScript reduce深入了解

reduce() 是 JavaScript 数组的一个高阶函数&#xff0c;它允许你将数组中的元素按顺序依次合并为一个单一的值。reduce() 可以用于数组求和、计算平均值、连接字符串等各种情况。它的工作原理是通过迭代数组的每个元素&#xff0c;然后将元素和累加器进行某种操作&#xff0c;…

使用 Python 在 NLP 中进行文本预处理

一、说明 自然语言处理 &#xff08;NLP&#xff09; 是人工智能 &#xff08;AI&#xff09; 和计算语言学的一个子领域&#xff0c;专注于使计算机能够理解、解释和生成人类语言。它涉及计算机和自然语言之间的交互&#xff0c;允许机器以对人类有意义和有用的方式处理、分析…

Java # JVM内存管理

一、运行时数据区域 程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、运行时常量池、直接内存 二、HotSpot虚拟机对象 对象创建&#xff1a; 引用检查类加载检查分配内存空间&#xff1a;指针碰撞、空闲列表分配空间初始化对象信息设置&#xff08;对象头内&#xff0…

​可视化绘图技巧100篇进阶篇(五)-阶梯线图(Step Chart)

目录 前言 图表类型特征 适用场景 图例 绘图工具及代码实现 ECharts SMARTBI

安卓中常见的字节码指令介绍

问题背景 安卓开发过程中&#xff0c;经常要通过看一些java代码对应的字节码&#xff0c;来了解java代码编译后的运行机制&#xff0c;本文将通过一个简单的demo介绍一些基本的字节码指令。 问题分析 比如以下代码&#xff1a; public class test {public static void main…

Java课题笔记~ JSP编程

4.1 JSP基本语法 JSP (全称Java Server Pages) 是由 Sun Microsystems 公司倡导和许多公司参与共同创建的一种使软件开发者可以响应客户端请求&#xff0c;而动态生成 HTML、XML 或其他格式文档的Web网页的技术标准。 JSPHTMLJava JSP的本质是Servlet 访问JSP的时候&#x…