C++11 适配器模式,MFC程序,也可以用控制台打印的

技术原理,使用一个
1、使用一个LineToPointCachingAdapter将Line适配成Point。
2、使用boost::hash生成对象特定的hash码,避免两次重复调用适配器,浪费资源。使用std::map做缓存

geometry.h

#pragma once
#include <vector>
#include <boost/functional/hash.hpp>

// Hash是为了避免缓存时重复计算
struct Point {
    int x, y;

    friend std::size_t hash_value(const Point& obj) {
        std::size_t seed = 0x725C686F;
        boost::hash_combine(seed, obj.x);
        boost::hash_combine(seed, obj.y);
        return seed;
    }
};

struct Line {
    Point start, end;

    friend std::size_t hash_value(const Line& obj) {
        std::size_t seed = 0x719E6B16;
        boost::hash_combine(seed, obj.start);
        boost::hash_combine(seed, obj.end);
        return seed;
    }
};

struct VectorObject {
    virtual std::vector<Line>::iterator begin() = 0;
    virtual std::vector<Line>::iterator end() = 0;
};

struct VectorRectangle : VectorObject {
    VectorRectangle(int x, int y, int width, int height) {
        lines.emplace_back(Line{ Point{x,y}, Point{x + width, y} });
        lines.emplace_back(Line{ Point{x+width, y}, Point{x+width, y+height} });
        lines.emplace_back(Line{ Point{x,y }, Point{x, y + height} });
        lines.emplace_back(Line{ Point{x, y + height}, Point{x + width, y + height} });
    }

    std::vector<Line>::iterator begin() override {
        return lines.begin();
    }

    std::vector<Line>::iterator end() override {
        return lines.end();
    }
        
private:
    std::vector<Line> lines;
};

DrawPointsDlg.h


// DrawPointsDlg.h: 头文件
//

#pragma once

#include "geometry.h"

#include <vector>

// CDrawPointsDlg 对话框
class CDrawPointsDlg : public CDialogEx
{
// 构造
public:
    CDrawPointsDlg(CWnd* pParent = nullptr);    // 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DRAWPOINTS_DIALOG };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

    void DrawPoints(CPaintDC& dc, std::vector<Point>::iterator start, std::vector<Point>::iterator end)
    {
        for (auto i = start; i != end; ++i)
            dc.SetPixel(i->x, i->y, 0);
    }

// 实现
protected:
    HICON m_hIcon;

    // 生成的消息映射函数
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
};

DrawPointsDlg.cpp


// DrawPointsDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "DrawPoints.h"
#include "DrawPointsDlg.h"
#include "afxdialogex.h"
#include <memory>
#include <map>
using namespace std;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

vector<shared_ptr<VectorObject>> vectorObjects{
    make_shared<VectorRectangle>(10, 10, 100, 100),
    make_shared<VectorRectangle>(30, 30, 60, 60)
};


struct LineToPointCachingAdapter {
    typedef vector<Point> Points;

    LineToPointCachingAdapter(Line& line) {
        boost::hash<Line> hash;
        line_hash = hash(line);
        // 已缓存,直接返回
        if (cache.find(line_hash) != cache.end()) {
            return;
        }

        static int count = 0;
        TRACE("%d: Generating points for line (with caching)\n", count++);
        
        Points points;

        int left = min(line.start.x, line.end.x);
        int right = max(line.start.x, line.end.x);
        int top = min(line.start.y, line.end.y);
        int bottom = max(line.start.y, line.end.y);
        int dx = right - left;
        int dy = line.end.y - line.start.y;

        // only vertical or horizontal lines
        if (dx == 0)
        {
            // vertical
            for (int y = top; y <= bottom; ++y)
            {
                points.emplace_back(Point{ left,y });
            }
        }
        else if (dy == 0)
        {
            for (int x = left; x <= right; ++x)
            {
                points.emplace_back(Point{ x, top });
            }
        }
        cache[line_hash] = points;
    }

    
    Points::iterator begin() {
        return cache[line_hash].begin();
    }

    Points::iterator end() {
        return cache[line_hash].end();
    }
private:
    size_t line_hash;
    static map<size_t, Points> cache;
};

map<size_t, vector<Point>> LineToPointCachingAdapter::cache;



// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_ABOUTBOX };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CDrawPointsDlg 对话框



CDrawPointsDlg::CDrawPointsDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DRAWPOINTS_DIALOG, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CDrawPointsDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CDrawPointsDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()


// CDrawPointsDlg 消息处理程序

BOOL CDrawPointsDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。

    // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != nullptr)
    {
        BOOL bNameValid;
        CString strAboutMenu;
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
        ASSERT(bNameValid);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE);         // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标

    // TODO: 在此添加额外的初始化代码

    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CDrawPointsDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialogEx::OnSysCommand(nID, lParam);
    }
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CDrawPointsDlg::OnPaint()
{
    
    if (IsIconic())
    {
        CPaintDC dc(this); // 用于绘制的设备上下文
        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // 使图标在工作区矩形中居中
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // 绘制图标
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CPaintDC dc(this); // 用于绘制的设备上下文
        for (auto& o : vectorObjects) {
            for (auto& l : *o) {
                LineToPointCachingAdapter lpo{ l };
                DrawPoints(dc, lpo.begin(), lpo.end());
            }
        }
        CDialogEx::OnPaint();
    }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CDrawPointsDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

程序输出如下,


图片.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 适配器模式是一种结构型设计模式,适配器模式主要是来解决接口不兼容的问题,使得原本没有关系的类可以协同工作。就好像我...
    TurboSnail阅读 328评论 0 3
  • 1 类适配器模式: 类的适配器模式把适配的类的API转换成为目标类的API。 在上图中可以看出,Adaptee类并...
    洋_6653阅读 1,733评论 0 1
  • 最近看到自己之前刚开始学习的时候记的一些笔记就稍微整理了一下 c语言里的结构体和c++里的结构体有什么区别? 答:...
    你猜卟透_faa8阅读 964评论 0 0
  • 安装最新版cmake & cmake https://blog.csdn.net/u011291667/artic...
    Jimmy_Tong阅读 1,272评论 0 0
  • 一 概述 定义:适配器模式将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能...
    时待吾阅读 499评论 0 0