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

网友投稿 282 2023-05-17

[[351273]]

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

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

示例:

复制// 来源:公众号【编程珠玑】  // 作者:守望先生  // api.h  #include<iostream>  struct Param{      int num;      std::string str;  };  void TestFun(const Param &param);  // api.cc  #include "api.h" void TestFun(const Param &param){      std::cout<<"num:"<<param.num<<" str:"<<param.str.c_str()<<std::endl;  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.

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

复制$ g++ -c api.cc -I./  $ ar -rcs libapi.a api.o   1.2.

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

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

复制// 来源:公众号编程珠玑  // 作者:守望先生  #include "api.h" int main(){      Param param;      param.num = 10;      param.str = "24";       TestFun(param);      return 0;  1.2.3.4.5.6.7.8.9.10.

编译链接使用:

复制$ g++ -o main main.cc -L./ -lapi -I ./  $ ./main  1.2.

看起来并没有什么问题,有新的参数,可以直接在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);  1.2.3.4.5.6.7.8.9.10.11.12.13.14.

也将它作为静态库:

复制$ g++ -c use_api.cc -I./  $ ar -rcs libuse_api.a use_api.o   1.2.

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

复制// 来源:公众号【编程珠玑】  // 作者:守望先生  // api.h  #include<iostream>  struct Param{      int num;      std::string str;      std::string ext;  };  void TestFun(const Param &param);  // api.cc  #include "api.h" void TestFun(const Param &param){      std::cout<<"num:"<<param.num<<" str:"<<param.str.c_str()<<" ext:"<<param.ext.c_str()<<std::endl;  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.

重新生成静态库:

复制$ g++ -c api.cc -I./  $ ar -rcs libapi.a api.o   1.2.

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

复制// 来源:公众号编程珠玑  // 作者:守望先生  #include "use_api.h" int main(){      UseApi();      return 0;  1.2.3.4.5.6.7.

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

复制$ g++ -o main main.cc -I./ -L./ -luse_api -lapi  $ ./main  Segmentation fault (core dumped)  1.2.3.

看到没有,喜闻乐见的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 &param);  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.

在这里头文件中只提供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 &param){      std::cout<<"num:"<<param.GetNum()<<" str:"<<param.GetStr().c_str()<<"ext:"<<param.GetExt().c_str()<<std::endl;  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.

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

总结

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

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

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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:如何快速查看自己的实名验证验证码__
下一篇:更新整理的一大波热门免费可用的API大全
相关文章

 发表评论

暂时没有评论,来抢沙发吧~