畅游模板编程--纯面向接口编程(无需显示继承,类 golang 隐式接口)

 

本文的目的

      实现类似于 golang 的隐式接口, 何为隐式接口, 那不得不先说下 c++ 的显示接口了, 我们知道 c++中,如果要实现一个接口必须显示的继承这个接口


#include <algorithm>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;

class speakBehavior {
public:
    virtual void speak() = 0;
    ~speakBehavior(){}
};


class dog : public speakBehavior {
public:
    void speak() override {
        cout << "汪汪" << endl;
    }
};

class cat : public speakBehavior {
public:
    void speak() override {
        cout << "喵喵" << endl;
    }
};

int main() {
   speakBehavior* sp = new dog;
   sp->speak();
   sp = new cat;
   sp->speak();
}

输出

畅游模板编程--纯面向接口编程(无需显示继承,类 golang 隐式接口)

我么你可以发现, dog, cat 都显示实现了speakBehavior 接口

对于相同的行为, golang 的接口隐式实现又是什么样的呢, golang 规定,只要函数签名一样以及函数名一样那么你就实现了该接口, 让我们看看 golang 相同的代码

package main

import "fmt"

type speakBehavior interface {
	speak()
}
type cat struct {
}
func(c cat) speak() {
	fmt.Println("喵喵")
}
type dog struct {
}
func(d dog) speak() {
	fmt.Println("汪汪")
}
func main() {
	var s speakBehavior
	s = dog{}
	s.speak()
	s = cat{}
	s.speak()
}

输出

畅游模板编程--纯面向接口编程(无需显示继承,类 golang 隐式接口)

 

发现差别没有, c++ 是必须实现接口才能 interface* in = new Obj

在 golang 中, 只要你实现了接口的所有方法那么你就 可以interface in = obj{}, how cool

现在进入本文正题, c++ 能否实现类似的接口呢?

当然借用函数指针就行, 请看下面的代码

畅游模板编程--纯面向接口编程(无需显示继承,类 golang 隐式接口)

这次我们也没有实现接口,最终结果和 golang 类似,我们发现我们更加的灵活,我们甚至能做到,函数名不一致我们也能调用, 见上面的 shoutLoud 和 speak

 

故事到这里对于一般人差不多,该结束了。

 

下面我们发散的聊聊, 上面我们使用 function 模板来实现类 golang 的编程方式,甚至我们更加凶猛, 甚至函数名不一样都可以, 这个在一定程度可能有隐患

那么我们能不能加上函数名必须一样的这种限制了,那么下面将会讲讲怎么实现

建议看看https://blog.csdn.net/qq_34179431/article/details/116305213 再往下读


#include <algorithm>
#include <iostream>

using namespace std;

class car {
public:
    void run() {
        cout << "i am a car, move move move" << endl;
    }

    void print(string str) {
        cout << "i am a car," << str << endl;
    }
};

class crazyDog {
public:
    void speak() {
        cout << "i am a dog, 汪汪汪" << endl;
    }

    void speak(string word) {
        cout << "i am a dog," << word << endl;
    }

    void run() {
        cout << "i am a dog, move move move" << endl;
    }
};

#define HAS_MEMBER(member) \
template<typename T, typename ... Args> \
struct has_member_##member {            \
 private:                      \
    template<typename U>                         \
    static auto Check(int)-> decltype(declval<U>().member(declval<Args>()...), true_type()); \
    template<typename U>                           \
    static false_type Check(...);        \
 public:                          \
    constexpr static bool value = is_same<true_type, decltype(Check<T>(0))>::value;          \
};

#define GET_SPECIFIC_CALLBACK(name) \
template<typename Obj, typename R, typename ...Args> \
auto name##CallBack(shared_ptr<Obj> obj) \
-> typename conditional< \
        is_same< \
                true_type, \
                typename conditional<has_member_##name<Obj, Args...>::value, true_type, false_type>::type \
        >::value, \
        function<R(Args...)>, \
        void \
>::type { \
    R(Obj::*fun)(Args...) = &Obj::name; \
    return [other = obj, f = fun](Args &&... args1) { \
        return ((*other).*f)(forward<Args>(args1)...); \
    }; \
}

#define REGISTER_CALLBACK(name) \
    HAS_MEMBER(name) \
    GET_SPECIFIC_CALLBACK(name)


REGISTER_CALLBACK(run)

REGISTER_CALLBACK(speak)

REGISTER_CALLBACK(print)

int main() {
  auto p = runCallBack<car, void>(make_shared<car>());
  p();

  p = runCallBack<crazyDog, void>(make_shared<crazyDog>());
  p();

  p = speakCallBack<crazyDog, void>(make_shared<crazyDog>());
  p();

  auto d = speakCallBack<crazyDog, void, string>(make_shared<crazyDog>());
  d("hello");

  d = printCallBack<car, void, string>(make_shared<car>());
  d("超速");

  //car 没有实现 speak 接口,故报错
  // p = speakCallBack<car, void>(make_shared<car>());
}

输出:

畅游模板编程--纯面向接口编程(无需显示继承,类 golang 隐式接口)

 

解释

1. 注册

REGISTER_CALLBACK(run) // 注册 run 方法 生成 runCallBack
REGISTER_CALLBACK(speak) // 注册 speak, 生成 speakCallBack 

2. 使用

// runCallBack<对象,返回值,参数列表>
auto p = runCallBack<car, void>(make_shared<car>());
p();

p = runCallBack<crazyDog, void>(make_shared<crazyDog>());
p();
p = speakCallBack<crazyDog, void>(make_shared<crazyDog>());
p();

auto d = speakCallBack<crazyDog, void, string>(make_shared<crazyDog>());
d("hello");

综上,代码完成了, 文字不多, 希望各路大佬海涵。

 

 

 

上一篇:纯js实现人脸识别眨眨眼张张嘴案例——ccv.js


下一篇:win10+VS2017配置ffmpeg