畅游模板编程--纯面向接口编程(无需显示继承,类 golang 隐式接口)
2021/5/13 22:30:21
本文主要是介绍畅游模板编程--纯面向接口编程(无需显示继承,类 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; } }; #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>()); }
输出:
解释
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");
综上,代码完成了, 文字不多, 希望各路大佬海涵。
这篇关于畅游模板编程--纯面向接口编程(无需显示继承,类 golang 隐式接口)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23MongoDB身份认证机制揭秘!
- 2024-11-20MongoDB教程:从入门到实践详解
- 2024-11-17执行 Google Ads API 查询后返回的是空数组什么原因?-icode9专业技术文章分享
- 2024-11-17google广告数据不同经理账户下的凭证可以获取对方的api数据吗?-icode9专业技术文章分享
- 2024-11-15SendGrid 的 Go 客户端库怎么实现同时向多个邮箱发送邮件?-icode9专业技术文章分享
- 2024-11-15SendGrid 的 Go 客户端库怎么设置header 和 标签tag 呢?-icode9专业技术文章分享
- 2024-11-12Cargo deny安装指路
- 2024-11-02MongoDB项目实战:从入门到初级应用
- 2024-11-01随时随地一键转录,Google Cloud 新模型 Chirp 2 让语音识别更上一层楼
- 2024-10-25Google Cloud动手实验详解:如何在Cloud Run上开发无服务器应用