C++ 11可變參數接口設計在模板編程中應用的一點點總結

概述

  本人對模板編程的應用並不是很深,若要用一句話總結我我的對模板編程的理解,我想說的是:模板編程是對類定義的弱化。ios

  如何理解「類定義的弱化」?c++

  一個完整的類有以下幾部分組成:編程

  1. 類的名稱;
  2. 類的成員變量(或屬性,C#中屬性和成員變量仍是有區別的);
  3. 類的成員方法;

  從編譯器的角度看,咱們必須明確指定以上3部分,纔算完整地定義了一個類而且編譯經過。函數

  所謂的「類弱化」,是指類的設計者在定義類的時候,並無完整定義一個類,而是把類的其中一部分的定義留給類的使用者。oop

  從傳統才c++98看,經過模板類,使用者能夠參與定義類的部分有:學習

  一、成員變量的類型;spa

  二、類成員方法參數的類型;設計

//實例代碼
template<class T> class Test { public: template<class Param>void TestWork(Param p) { } private: T m_t; };

  模板功能大大提升了類設計者代碼的可拓展性和可維護性。調試

  OK咱們切入主題,C++11可變參數接口在「類弱化」方面有什麼提升呢?code

  總結地來講,咱們能夠增長一點,使用者能夠參與定義類的部分有:

  三、類成員方法函數的參數個數。

 


 

  本總結包含以下兩部分:

  1.  C++11可變參數設計的簡單介紹。
  2.  經過「觀察者模式」示例進一步瞭解C++11可變參數設計在實際編程中的應用。
    1. 使用可變參數前
    2. 使用可變參數後
  3. 總結

 

C++11可變參數設計的簡單介紹

  咱們下面經過一個簡單的代碼來認識可變參數的「形狀」:

template<typename ... Args> void Test(Args ... args)
{
}
int main(int argc, char* argv[])
{
    Test(1, 2, 3, 4, "abc");
}

  以上代碼很是簡單,咱們在main函數中調用Test函數時,能夠傳遞任意多個參數,且參數的類型也是任意的。若是咱們在VS(VS2010以上版本)下斷點調試,咱們能夠看到Test函數的參數。其書寫形式也很是簡單,C++11拓展了模板的書寫形式,typename ... Args」就能夠定義一個可變從參數類型。

 做爲簡單的介紹,我以爲再也已經沒有什麼能夠在說的了,網上也有不少例子,經典例子就是咱們把傳給Test函數的值都打印出來,代碼以下:
#include <iostream>
template<typename T> void Test(T t) //當參數個數等於1是,進入此函數
{
    std::cout << t << std::endl;
}
template<typename T,typename ... Args> void Test(T t, Args ... args)  //當參數個數大於1是,進入此函數
{
    std::cout << t << std::endl;
    Test(args...);  //循環調用Test函數
}
int main(int argc, char* argv[])
{
    Test(1, 2, 3, 4, "abc");
}

 

經過「觀察者模式」示例進一步C++11可變參數設計在實際編程中的應用。

   下面咱們經過鼠標消息事件分發實例,不使用可變參數列表和使用可變參數列表設計對比兩者實現效果的差異。

  1、使用可變參數

#include <list>
#include <iostream>
#include <mutex>
#include <algorithm> 
class IListener  //監聽者接口類
{
public:
    virtual void OnPress(double dXpos, double dYpos) = 0;
    virtual void OnCallBack(std::string sParam,int iParam) = 0;
};

template<class T>
class DispatcherBase
{
public:
    std::list<T*>& GetListeners()
    {
        return m_listeners;
    }

    void AddListener(T* tparam)//一、添加註冊listener
    {
        auto itor = std::find_if(m_listeners.begin(), m_listeners.end(), find_listener(tparam));
        if (itor != m_listeners.end()) return;
        m_listeners.push_back(tparam);
    }

    void RemoveListener(T* tparam) //二、移除註冊的listener
    {
        auto itor = std::find_if(m_listeners.begin(), m_listeners.end(), find_listener(tparam));
        if (itor == m_listeners.end()) return;
        m_listeners.erase(itor);
    }

protected:
    std::list<T*> m_listeners; //三、listener的集合
    struct find_listener//四、listener查找比較規則定義
    {
        find_listener(T* listener) :m_listener(listener) {}
        bool operator()(const T* listener)
        {
            if (m_listener == listener) return true;
            else return false;
        }
        T* m_listener;
    };
    std::mutex m_mtx;
};

class Dispatcher : public DispatcherBase<IListener> //消息分發類
{
public:
    void DispatcherPress(double dXpos, double dYpos)
    {
        m_mtx.lock();
        auto listners = GetListeners();
        for (auto itor : listners)
        {
            itor->OnPress(dXpos, dYpos);
        }
        m_mtx.unlock();
    }

    void DispatcherCallBack(std::string sParam, int iParam)
    {
        m_mtx.lock();
        auto listners = GetListeners();
        for (auto itor : listners)
        {
            itor->OnCallBack(sParam, iParam);
        }
        m_mtx.unlock();
    }
};

class ListenerTest : public IListener  //監聽者類
{
public:
    ListenerTest(std::string sName)
     : m_sName(sName)
    {
    }
    virtual void OnPress(double dXpos, double dYpos)
    {
        std::cout <<"OnPress:"<< m_sName << std::endl;
    }
    virtual void OnCallBack(std::string sParam, int iParam)
    {
        std::cout <<"OnCallBack:"<< m_sName << std::endl;
    }
private:
    std::string m_sName;
};

int main()
{
    Dispatcher dispatcher; //消息分發者實例
    ListenerTest listener1("listener1"); //消息監聽者實例
    ListenerTest listener2("listener2"); //消息監聽者實例
    dispatcher.AddListener(&listener1);
    dispatcher.AddListener(&listener2);

    dispatcher.DispatcherCallBack("callbackEvent",1); //執行消息分發動做
    dispatcher.DispatcherPress(10, 10); //執行消息分發動做
}

  運行結果以下:    

      

  上面代碼爲很是經常使用的觀察者模式設計的代碼,若是您以爲以上代碼難懂,直接拷貝到電腦上調試一下會相對容易些。


 

  咱們來總結以上代碼的問題點:

  以下紅色代碼段重複代碼是咱們常常看到,但在C++11以前確又沒法解決的問題。

void DispatcherPress(double dXpos, double dYpos)
{
 m_mtx.lock(); auto listners = GetListeners(); for (auto itor : listners) {
        itor->OnPress(dXpos, dYpos);
 } m_mtx.unlock();
}
void DispatcherCallBack(std::string sParam, int iParam)
{ m_mtx.
lock(); auto listners = GetListeners(); for (auto itor : listners) { itor->OnCallBack(sParam, iParam); } m_mtx.unlock(); }

 

  2、使用可變參數後

  下面,咱們就來使用可變參數的方法簡化以上的問題:

#pragma once
#include <list>
#include <functional>
#include <algorithm>
#include <list>
#include <iostream>
#include <mutex>
#include <algorithm> 
class IListener
{
public:
    virtual void OnPress(double dXpos, double dYpos) = 0;
    virtual void OnCallBack(std::string sParam,int iParam) = 0;
};

template<class T>
class Dispatcher
{
public:
 template<typename Funtype, typename... Args> void Dispatch(Funtype funtype, Args... args) //可變參數修改後的消息分發事件 { m_mtx.lock(); for (auto &itor : m_listeners) { std::bind(funtype, itor, args...)(); //使用std::bind去綁定具體對象,並調該對象的方法 } m_mtx.unlock(); } void AddListener(T* tparam)
    {
        auto itor = std::find_if(m_listeners.begin(), m_listeners.end(), find_listener(tparam));
        if (itor != m_listeners.end()) return;
        m_listeners.push_back(tparam);
    }

    void RemoveListener(T* tparam)
    {
        auto itor = std::find_if(m_listeners.begin(), m_listeners.end(), find_listener(tparam));
        if (itor == m_listeners.end()) return;
        m_listeners.erase(itor);
    }
protected:
    std::list<T*> m_listeners;
    struct find_listener
    {
        find_listener(T* listener) :m_listener(listener) {}
        bool operator()(const T* listener)
        {
            if (m_listener == listener) return true;
            else return false;
        }
        T* m_listener;
    };
    std::mutex m_mtx;
};

class ListenerTest : public IListener
{
public:
    ListenerTest(std::string sName)
        : m_sName(sName)
    {
    }
    virtual void OnPress(double dXpos, double dYpos)
    {
        std::cout << "OnPress:" << m_sName << std::endl;
    }
    virtual void OnCallBack(std::string sParam, int iParam)
    {
        std::cout << "OnCallBack:" << m_sName << std::endl;
    }
private:
    std::string m_sName;
};

int main()
{
    Dispatcher<IListener> dispatcher;
    ListenerTest listener1("listener1");
    ListenerTest listener2("listener2");
    dispatcher.AddListener(&listener1);
    dispatcher.AddListener(&listener2);
    dispatcher.Dispatch(&IListener::OnCallBack,"callbackEvent", 1); 
    dispatcher.Dispatch(&IListener::OnPress, 10, 10);
}

  以上代碼運行結果與以前同樣,可是咱們發現,不一樣的消息分發的時候,咱們都是調用Dispatch函數進行分發,咱們省去了爲每一個消息從新定義一個分發事件函數的部分。Dispatch函數的內部實現使用了可變參數的接口設計方法。

 

總結

  咱們從簡單的C++可變參數使用,到實際使用示例講解,瞭解到了C++11可變參數在模板編程方面應用的效果是很是棒的。

  涉及知識點總結:

  1. C++11可變參數寫法;
  2. std::bind方法的使用;
  3. std::mutex方法的使用;

  到此,咱們的文章到此結束,感謝你們的學習,更多優秀設計請持續關注個人博客。

相關文章
相關標籤/搜索