双缓冲
双缓冲在之前写字符雨的时候,已经简单介绍过,今天我们来写一个简单的程序来体会双缓冲机制
我们实现一个在屏幕上画直线的功能:
在类中添加变量,保存起点坐标和终点坐标:
cpp
//定义一个容器,保存每次画的直线
using Lint = std::pair(CPoint,CPoint);
CList<>m_List;CPoint m_begin;
CPoint m_end;
在对话框上添加WM_MOUSEMOVE,WM_LBUTTONDOWM,WM_LBUTTONUP消息处理函数:
cpp
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);
}
cpp
void C双缓冲::OnMouseMove(UINT nFlags,CPoint point){
if(nFlags&MK_LBUTTON){
m_end = point;
IncalidateRect(NULL,TRUE);
}
CView::OnMouseMoce(nFlags,point);
}
cpp
void C双缓冲View::OnLButtonDown(UINT nFlags,CPoint point){
//记录开始坐标
CVIew::OnLButtonDown(nFlags,point);
m_begin = point;
}
cpp
void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){
//记录终点坐标
m_end = point;
CVIew::OnLButtonUp(nFlags,point);
ReleaseCapture();
}
这样写完之后,没有反应,这是因为没有无效区域,我们将OnLButtonUp函数中添加无效区域就可以了:
cpp
void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){
//记录终点坐标
m_end = point;
CVIew::OnLButtonUp(nFlags,point);
InvalidateRect(NULL,TRUE);
}
只有有了无效区域,绘图消息才会产生
然后我们完善:
cpp
//定义一个容器,保存每次画的直线
using Lint = std::pair(CPoint,CPoint);
CList<>m_List;
cpp
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);
}
cpp
void C双缓冲::OnMouseMove(UINT nFlags,CPoint point){
if(nFlags&MK_LBUTTON){
m_end = point;
IncalidateRect(NULL,TRUE);
}
CView::OnMouseMoce(nFlags,point);
}
cpp
void C双缓冲View::OnLButtonDown(UINT nFlags,CPoint point){
//记录开始坐标
CVIew::OnLButtonDown(nFlags,point);
m_begin = point;
//捕捉客户区外鼠标消息
SetCapture();
}
cpp
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
然后我们修改代码:
cpp
void C双缓冲View::OnDraw(UINT nFlags,CPoint point){
C双缓冲View* pDoc = GetDocument();
ASSERT_CALID(pDoc);
if(!pFoc)
return;
//双缓冲绘图
//1.创建内存DC
CDC 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就还是什么流程
cppvoid C双缓冲View::OnDraw(UINT nFlags,CPoint point){ C双缓冲View* pDoc = GetDocument(); ASSERT_CALID(pDoc); if(!pFoc) return; //双缓冲绘图 //1.创建内存DC CDC 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)); //把画笔送到内存DC deMem.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); }
序列化
-
为什么要有序列化:
我们在绘图应用程序上绘制的图形,可以保存起来,我们之后还可以打开
而我们上面写的程序,是不能保存的,这就是序列化的基本功能
-
简单使用以下序列化:
- 首先继承于CObject
- 类内添加声明宏DECLARE_SERTAL
- 类外实现宏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<<x<<y<<str; } } int x; int y; int color; CString str; DECLARE_SERTAL(CLine) } IMPLEMENT_SERIAL(CLine,Cobject,1)
cppint 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 归档类对象
cpp
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;
}
}
}
反序列化:
cpp
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;
}
cpp
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;
}
}
}
了解了序列化和反序列化过程,我们就可以将我们画的图保存起来了:
-
添加一个直线类
cpp#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;
-
我们将OnDraw消息稍作修改:就是直线的数据获取位置不一样了,我们需要修改:
cpp
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);
}
- 这样修改之后,我们还需要将直线类的拷贝构造函数修改,因为默认是浅拷贝,我们在OnDraw函数中使用的时候,显示已经释放:
cpp
#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;
};
- 然后我们去文档类中处理:
cpp
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);
}
}
}
}
双缓冲
双缓冲在之前写字符雨的时候,已经简单介绍过,今天我们来写一个简单的程序来体会双缓冲机制
我们实现一个在屏幕上画直线的功能:
在类中添加变量,保存起点坐标和终点坐标:
cpp
//定义一个容器,保存每次画的直线
using Lint = std::pair(CPoint,CPoint);
CList<>m_List;CPoint m_begin;
CPoint m_end;
在对话框上添加WM_MOUSEMOVE,WM_LBUTTONDOWM,WM_LBUTTONUP消息处理函数:
cpp
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);
}
cpp
void C双缓冲::OnMouseMove(UINT nFlags,CPoint point){
if(nFlags&MK_LBUTTON){
m_end = point;
IncalidateRect(NULL,TRUE);
}
CView::OnMouseMoce(nFlags,point);
}
cpp
void C双缓冲View::OnLButtonDown(UINT nFlags,CPoint point){
//记录开始坐标
CVIew::OnLButtonDown(nFlags,point);
m_begin = point;
}
cpp
void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){
//记录终点坐标
m_end = point;
CVIew::OnLButtonUp(nFlags,point);
ReleaseCapture();
}
这样写完之后,没有反应,这是因为没有无效区域,我们将OnLButtonUp函数中添加无效区域就可以了:
cpp
void C双缓冲View::OnLButtonUp(UINT nFlags,CPoint point){
//记录终点坐标
m_end = point;
CVIew::OnLButtonUp(nFlags,point);
InvalidateRect(NULL,TRUE);
}
只有有了无效区域,绘图消息才会产生
然后我们完善:
cpp
//定义一个容器,保存每次画的直线
using Lint = std::pair(CPoint,CPoint);
CList<>m_List;
cpp
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);
}
cpp
void C双缓冲::OnMouseMove(UINT nFlags,CPoint point){
if(nFlags&MK_LBUTTON){
m_end = point;
IncalidateRect(NULL,TRUE);
}
CView::OnMouseMoce(nFlags,point);
}
cpp
void C双缓冲View::OnLButtonDown(UINT nFlags,CPoint point){
//记录开始坐标
CVIew::OnLButtonDown(nFlags,point);
m_begin = point;
//捕捉客户区外鼠标消息
SetCapture();
}
cpp
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
然后我们修改代码:
cpp
void C双缓冲View::OnDraw(UINT nFlags,CPoint point){
C双缓冲View* pDoc = GetDocument();
ASSERT_CALID(pDoc);
if(!pFoc)
return;
//双缓冲绘图
//1.创建内存DC
CDC 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就还是什么流程
cppvoid C双缓冲View::OnDraw(UINT nFlags,CPoint point){ C双缓冲View* pDoc = GetDocument(); ASSERT_CALID(pDoc); if(!pFoc) return; //双缓冲绘图 //1.创建内存DC CDC 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)); //把画笔送到内存DC deMem.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); }
序列化
-
为什么要有序列化:
我们在绘图应用程序上绘制的图形,可以保存起来,我们之后还可以打开
而我们上面写的程序,是不能保存的,这就是序列化的基本功能
-
简单使用以下序列化:
- 首先继承于CObject
- 类内添加声明宏DECLARE_SERTAL
- 类外实现宏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 归档类对象
cpp
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;
}
}
}
反序列化:
cpp
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;
}
cpp
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;
}
}
}
了解了序列化和反序列化过程,我们就可以将我们画的图保存起来了:
-
添加一个直线类
cpp#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;
-
我们将OnDraw消息稍作修改:就是直线的数据获取位置不一样了,我们需要修改:
cpp
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);
}
- 这样修改之后,我们还需要将直线类的拷贝构造函数修改,因为默认是浅拷贝,我们在OnDraw函数中使用的时候,显示已经释放:
cpp
#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;
};
- 然后我们去文档类中处理:
cpp
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);
}
}
}
}