畅游模板编程--纯面向接口编程(无需显示继承,类 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();
}

输出

我么你可以发现, 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()
}

输出

 

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

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

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

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

这次我们也没有实现接口,最终结果和 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;
    }
};

void run(string str) {
    cout << "i am " << str << " move move move" << endl;
}

#define REGISTER_CALLBACK(name)\
template<typename Obj, typename ...Args> \
auto has_##name(int) ->decltype(declval<Obj>().name(declval<Args>...), true_type{}); \
                               \
template<typename Obj, typename ...Args> \
false_type has_##name(...); \
                               \
template<typename Obj, typename ...Args> \
using name##able = typename std::is_same<decltype(has_##name<Obj>(0)), true_type>::type; \
                               \
template<typename Obj, typename R, typename ...Args> \
auto name##CallBack(shared_ptr<Obj> obj) \
-> typename conditional< \
        is_same< \
                true_type , \
                name##able<Obj> \
        >::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)...); \
    }; \
} \
                               \
template<typename R,typename ...Args> \
auto name##CallBack() -> typename conditional< \
        is_same< \
                true_type , \
                typename conditional<is_same< \
                        R, \
                        decltype( \
                                name(declval<Args>()...) \
                                )>::value, true_type, false_type>::type \
        >::value, \
        function<R(Args...)>, \
        void \
>::type { \
    return [](Args&& ... args1) { \
        return name(forward<Args>(args1)...); \
    }; \
}

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();

  auto sp = speakCallBack<crazyDog, void>(make_shared<crazyDog>());
  sp();

  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>());

  auto rs = runCallBack<void, string>();
  rs("human");

}

输出:

 

解释

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");

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

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值