文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

API这样设计?等着程序挂掉吧!

2024-12-03 17:16

关注

本文转载自微信公众号「编程珠玑」,作者守望先生。转载本文请联系编程珠玑公众号。  

假设提供的接口的入参比较复杂,可能有人会考虑使用结构体作为入参。当你考虑这么做的时候,灾难也将会随之而来……

示例:

  1. // 来源:公众号【编程珠玑】 
  2. // 作者:守望先生 
  3. // api.h 
  4. #include 
  5. struct Param{ 
  6.     int num; 
  7.     std::string str; 
  8. }; 
  9. void TestFun(const Param ¶m); 
  10.  
  11. // api.cc 
  12. #include "api.h" 
  13. void TestFun(const Param ¶m){ 
  14.     std::cout<<"num:"<" str:"<

假设提供TestFun作为一个对外接口,我们编译并制作为静态库:

  1. $ g++ -c api.cc -I./ 
  2. $ ar -rcs libapi.a api.o  

关于静态库的制作,请参考《Linux下如何制作静态库?》。

另外一个程序main.cc这么使用它:

  1. // 来源:公众号编程珠玑 
  2. // 作者:守望先生 
  3. #include "api.h" 
  4. int main(){ 
  5.     Param param; 
  6.     param.num = 10; 
  7.     param.str = "24";  
  8.     TestFun(param); 
  9.     return 0; 

编译链接使用:

  1. $ g++ -o main main.cc -L./ -lapi -I ./ 
  2. $ ./main 

看起来并没有什么问题,有新的参数,可以直接在Param中增加即可,扩展性也不错。

问题来了

目前来看是没有什么问题的,但是假设,还有另外一个库要使用它,例如:

  1. // 来源:公众号编程珠玑 
  2. // 作者:守望先生 
  3. // use_api.h 
  4. #include"api.h" 
  5. void UseApi(); 
  6.  
  7. // use_api.cc 
  8. #include"use_api.h" 
  9. void UseApi(){ 
  10.     Param param; 
  11.     param.num = 10; 
  12.     param.str = "24";  
  13.     TestFun(param); 

也将它作为静态库:

  1. $ g++ -c use_api.cc -I./ 
  2. $ ar -rcs libuse_api.a use_api.o  

这个时候同样主程序会用到我们的原始api,但是却使用了不同的版本,比如,新增了Param中新增了一个字段ext:

  1. // 来源:公众号【编程珠玑】 
  2. // 作者:守望先生 
  3. // api.h 
  4. #include 
  5. struct Param{ 
  6.     int num; 
  7.     std::string str; 
  8.     std::string ext; 
  9. }; 
  10. void TestFun(const Param ¶m); 
  11.  
  12. // api.cc 
  13. #include "api.h" 
  14. void TestFun(const Param ¶m){ 
  15.     std::cout<<"num:"<" str:"<" ext:"<

重新生成静态库:

  1. $ g++ -c api.cc -I./ 
  2. $ ar -rcs libapi.a api.o  

这个时候,通过use_api使用api接口,但是链接新的库:

  1. // 来源:公众号编程珠玑 
  2. // 作者:守望先生 
  3. #include "use_api.h" 
  4. int main(){ 
  5.     UseApi(); 
  6.     return 0; 

这个时候,再去编译链接,并运行:

  1. $ g++ -o main main.cc -I./ -L./ -luse_api -lapi 
  2. $ ./main 
  3. Segmentation fault (core dumped) 

看到没有,喜闻乐见的core dumped了,分析core还会发现,是由于访问非法地址导致的。

我们再来梳理一下这个过程:

这个时候,版本B的实现访问了新的字段,还是use_api中还是使用A版本,并没有传入新字段,因此自然会导致非法访问。

如何解决?

很简单,不直接暴露成员,而是提供setter和getter,而提供方式和前面提到的PIMPL方法类似。

  1. // api.h 
  2. // 来源:公众号编程珠玑 
  3. // 作者:守望先生 
  4. #include 
  5. #include 
  6. class Param{ 
  7. public
  8.     void SetNum(int num); 
  9.     int GetNum() const; 
  10.     void SetStr(const std::string &str); 
  11.     std::string GetStr() const; 
  12.     void SetExt(const std::string &str); 
  13.     std::string GetExt() const; 
  14.     Param(); 
  15.   private: 
  16.     class ParamImpl; 
  17.     std::unique_ptr param_impl_; 
  18. }; 
  19. void TestFun(const Param ¶m); 

在这里头文件中只提供setter和getter,而完全不暴露成员,具体成员的设置在ParamImpl中实现:

  1. // api.cc 
  2. // 来源:公众号编程珠玑 
  3. // 作者:守望先生 
  4. #include "api.h" 
  5. class Param::ParamImpl{ 
  6.   public
  7.     int num; 
  8.     std::string str; 
  9.     std::string ext; 
  10. }; 
  11. Param::Param(){ 
  12.     param_impl_.reset(new ParamImpl); 
  13. // 析构函数必须要 
  14. Param::~Param() = default
  15. void Param::SetNum(int num){ 
  16.     param_impl_->num = num; 
  17. int Param::GetNum() const { 
  18.     return  param_impl_->num; 
  19. void Param::SetStr(const std::string &str){ 
  20.     param_impl_->str = str; 
  21. void Param::SetExt(const std::string &ext){ 
  22.     param_impl_->ext = ext; 
  23. std::string Param::GetStr() const { 
  24.     return param_impl_->str; 
  25. std::string Param::GetExt() const { 
  26.     return param_impl_->ext; 
  27. void TestFun(const Param ¶m){ 
  28.     std::cout<<"num:"<" str:"<"ext:"<

通过上面的方式,不会直接暴露成员函数,而是提供接口设置或者获取,而在实现中,即便出现新的版本增加了接口,最多也只是获取到默认值,而不会导致程序崩溃。

总结

本文和之前的文章实现方法是一样的,这样不暴露成员的做法,更大程度避免了链接库不一致导致的问题,你学会了吗?

作者:守望,linux应用开发者,目前在公众号【编程珠玑】?分享Linux/C/C++/数据结构与算法/工具等原创技术文章和学习资源。

原文链接:https://mp.weixin.qq.com/s/3SmRDVzDq6NCBTeVPTwiWQ

 

来源:编程珠玑内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯