API这样设计?等着程序挂掉吧!
source link: https://mp.weixin.qq.com/s?__biz=MzI2OTA3NTk3Ng%3D%3D&%3Bmid=2649286386&%3Bidx=1&%3Bsn=16a5b5d12dd3866c20b71d797598bf44
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
来源:公众号【编程珠玑】
作者:守望先生
ID:shouwangxiansheng
之前在《 PIMPL-隐藏类的私有成员 》中介绍了一种隐藏类的私有成员的方法,或者说隐藏接口实现细节的方法-PIMPL。
假设提供的接口的入参比较复杂,可能有人
会考虑使用结构体作为入参。当你考虑这么做的时候,灾难也将会随之而来
……
示例:
// 来源:公众号【编程珠玑】 // 作者:守望先生 // api.h #include<iostream> struct Param{ int num; std::string str; }; void TestFun(const Param ¶m); // api.cc #include "api.h" void TestFun(const Param ¶m){ std::cout<<"num:"<<param.num<<" str:"<<param.str.c_str()<<std::endl; }
假设提供TestFun作为一个对外接口,我们编译并制作为静态库:
$ g++ -c api.cc -I./ $ ar -rcs libapi.a api.o
关于静态库的制作,请参考《 Linux下如何制作静态库? 》。
另外一个程序main.cc这么使用它:
// 来源:公众号编程珠玑 // 作者:守望先生 #include "api.h" int main(){ Param param; param.num = 10; param.str = "24"; TestFun(param); return 0; }
编译链接使用:
$ g++ -o main main.cc -L./ -lapi -I ./ $ ./main
看起来并没有什么问题,有新的参数,可以直接在Param中增加即可,扩展性也不错。
问题来了
目前来看是没有什么问题的,但是假设,还有另外一个库要使用它,例如:
// 来源:公众号编程珠玑 // 作者:守望先生 // use_api.h #include"api.h" void UseApi(); // use_api.cc #include"use_api.h" void UseApi(){ Param param; param.num = 10; param.str = "24"; TestFun(param); }
也将它作为静态库:
$ g++ -c use_api.cc -I./ $ ar -rcs libuse_api.a use_api.o
这个时候同样主程序会用到我们的原始api,但是却使用了不同的版本,比如,新增了Param中新增了一个字段ext:
// 来源:公众号【编程珠玑】 // 作者:守望先生 // api.h #include<iostream> struct Param{ int num; std::string str; std::string ext; }; void TestFun(const Param ¶m); // api.cc #include "api.h" void TestFun(const Param ¶m){ std::cout<<"num:"<<param.num<<" str:"<<param.str.c_str()<<" ext:"<<param.ext.c_str()<<std::endl; }
重新生成静态库:
$ g++ -c api.cc -I./ $ ar -rcs libapi.a api.o
这个时候,通过use_api使用api接口,但是链接新的库:
// 来源:公众号编程珠玑 // 作者:守望先生 #include "use_api.h" int main(){ UseApi(); return 0; }
这个时候,再去编译链接,并运行:
$ g++ -o main main.cc -I./ -L./ -luse_api -lapi $ ./main Segmentation fault (core dumped)
看到没有,喜闻乐见的core dumped了,分析core还会发现,是由于访问非法地址导致的。
我们再来梳理一下这个过程:
-
提供库libapi.a版本A
-
libuse_api使用版本A进行编译,使用A版本的头文件
-
libapi.a库升级到B版本,其中头文件中增加了字段,并且实现也引用了新的字段
-
主程序使用了use_api,但是链接了版本B的libapi.a库
这个时候,版本B的实现访问了新的字段,还是use_api中还是使用A版本,并没有传入新字段,因此自然会导致非法访问。
如何解决?
很简单,不直接暴露成员,而是提供setter和getter,而提供方式和前面提到的PIMPL方法类似。
// api.h // 来源:公众号编程珠玑 // 作者:守望先生 #include<iostream> #include<memory> class Param{ public: void SetNum(int num); int GetNum() const; void SetStr(const std::string &str); std::string GetStr() const; void SetExt(const std::string &str); std::string GetExt() const; Param(); private: class ParamImpl; std::unique_ptr<ParamImpl> param_impl_; }; void TestFun(const Param ¶m);
在这里头文件中只提供setter和getter,而完全不暴露成员,具体成员的设置在ParamImpl中实现:
// api.cc // 来源:公众号编程珠玑 // 作者:守望先生 #include "api.h" class Param::ParamImpl{ public: int num; std::string str; std::string ext; }; Param::Param(){ param_impl_.reset(new ParamImpl); } // 析构函数必须要 Param::~Param() = default; void Param::SetNum(int num){ param_impl_->num = num; } int Param::GetNum() const { return param_impl_->num; } void Param::SetStr(const std::string &str){ param_impl_->str = str; } void Param::SetExt(const std::string &ext){ param_impl_->ext = ext; } std::string Param::GetStr() const { return param_impl_->str; } std::string Param::GetExt() const { return param_impl_->ext; } void TestFun(const Param ¶m){ std::cout<<"num:"<<param.GetNum()<<" str:"<<param.GetStr().c_str()<<"ext:"<<param.GetExt().c_str()<<std::endl; }
通过上面的方式,不会直接暴露成员函数,而是提供接口设置或者获取,而在实现中,即便出现新的版本增加了接口,最多也只是获取到默认值,而不会导致程序崩溃。
总结
本文和之前的文章实现方法是一样的,这样不暴露成员的做法,更大程度避免了链接库不一致导致的问题,你学会了吗?
示例代码可通过阅读原文查看。
相关精彩推荐
关注公众号【编程珠玑】,获取更多Linux/C/C++/数据结构与算法/计算机基础/工具等原创技术文章。 后台免费获取经典电子书和视频资源
Recommend
-
45
求职 - @auroraccc - 17 届毕业 , 实习写 vue hybrid , 现在写 react , 现在这份儿工作主要是从零开始写了一个管理系统然后写一些 rn 内的 web 页面 , 对 rn 和 electron 的熟悉
-
22
Hello,大家好,我是阿粉,作为一个后端工程师不经历几次生产事故怎么能成长!阿粉工作几年来,大大小小,重要不重要的事故也经历了不少,有损失几十万的,有对业务毫无影响但是不应该发生的,每一次事故都是一次成长,而且从每次的事故中...
-
5
vfork 挂掉的一个问题 浏览:1330次 出处信息 在知乎上,有个人问了这样的
-
6
记第一次把系统搞挂掉发表于2020-11-08更新于2020-11-08阅读次数149高度的自由有时就意味着高度的脆弱性。 今天就因为更换显卡驱动的原因把我实验室的机器给搞挂了,导致无法进入系统。 在实验室的机器到...
-
9
产品公开课 | 为什么你面试B端产品经理总是挂掉?15年+产品老司机说出了问题所在 | 人人都是产品经理¥1 ¥68 01 天 00 小时 31 分 08 秒 大咖分享 产品运营人必听 限时优惠 还剩 76...
-
16
shell脚本监控进程挂掉自动重启2019年7月12日 by anzhihe·0评论 · 957 人阅读 · 隐藏边栏
-
7
V2EX › 程序员 Go 实现的固定 routine pool, 挂掉重新拉起新 routine hulk ·
-
11
V2EX › 程序员 关于亚马逊 loop 面试挂掉问题咨询 partystart · 22 小时 18 分钟前 ·...
-
10
亚马逊又开始扫号了?一早挂掉20个账号,真的会关掉1W店铺吗? ...
-
6
违反这些设计原则,系统就等着“腐烂” 分...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK