C++最佳实践 | 3. 安全性

举报
C语言与CPP编程 发表于 2022/09/30 01:04:06 2022/09/30
【摘要】 本系列是开源书C++ Best Practises[1]的中文版,全书从工具、代码风格、安全性、可维护性、可移植性、多线程、性能、正确性等角度全面介绍了现代C++项目的最佳实践。本文是该系列的第三篇。 C++最佳实践: 1. 工具 2. 代码风格 3. 安全性(本文) 4. 可维护性 5...

本系列是开源书C++ Best Practises[1]的中文版,全书从工具、代码风格、安全性、可维护性、可移植性、多线程、性能、正确性等角度全面介绍了现代C++项目的最佳实践。本文是该系列的第三篇。

C++最佳实践:

1. 工具

2. 代码风格

3. 安全性(本文)

4. 可维护性

5. 可移植性及多线程

6. 性能

7. 正确性和脚本

安全性

尽量使用const

const修饰变量或方法,从而告诉编译器这些都是不可变的,有助于编译器优化代码,并帮助开发人员了解函数是否有副作用。此外,使用const &可以防止编译器复制不必要的数据。John Carmack对```const```的评论[2]值得一读。


   
  1. // Bad Idea
  2. class MyClass
  3. {
  4. public:
  5.   void do_something(int i);
  6.   void do_something(std::string str);
  7. };
  8. // Good Idea
  9. class MyClass
  10. {
  11. public:
  12.   void do_something(const int i);
  13.   void do_something(const std::string &str);
  14. };

仔细考虑返回类型

  • Getters(成员变量读取API)

    • 正常情况下,通过返回值读取成员变量时,使用&const &返回值可以显著提高性能

    • 按值返回更有利于线程安全,如果返回的值就是为了复制使用,就不会有性能损耗

    • 如果API返回值使用协变类型(covariant return types),必须返回&*

  • 临时值和局部值

    • 始终按值返回

参考:
https://github.com/lefticus/cppbestpractices/issues/21
https://twitter.com/lefticus/status/635943577328095232

不要用const引用传递和返回简单类型


   
  1. // Very Bad Idea
  2. class MyClass
  3. {
  4. public:
  5.   explicit MyClass(const int& t_int_value)
  6.     : m_int_value(t_int_value)
  7.   {
  8.   }
  9.   const int& get_int_value() const
  10.   {
  11.     return m_int_value;
  12.   }
  13. private:
  14.   int m_int_value;
  15. }

相反,通过值传递和返回简单类型。如果不打算更改传递的值,请将它们声明为const,但不要声明为const引用:


   
  1. // Good Idea
  2. class MyClass
  3. {
  4. public:
  5.   explicit MyClass(const int t_int_value)
  6.     : m_int_value(t_int_value)
  7.   {
  8.   }
  9.   int get_int_value() const
  10.   {
  11.     return m_int_value;
  12.   }
  13. private:
  14.   int m_int_value;
  15. }

为什么要这样?因为通过引用传递和返回会导致指针操作,而值传递在处理器寄存器中处理,速度更快。

避免访问裸内存

C++中很难在没有内存错误和泄漏风险[3]的情况下正确处理裸内存的访问、分配和回收,C++11提供了避免这些问题的工具。


   
  1. // Bad Idea
  2. MyClass *myobj = new MyClass;
  3. // ...
  4. delete myobj;
  5. // Good Idea
  6. auto myobj = std::make_unique<MyClass>(constructor_param1, constructor_param2); // C++14
  7. auto myobj = std::unique_ptr<MyClass>(new MyClass(constructor_param1, constructor_param2)); // C++11
  8. auto mybuffer = std::make_unique<char[]>(length); // C++14
  9. auto mybuffer = std::unique_ptr<char[]>(new char[length]); // C++11
  10. // or for reference counted objects
  11. auto myobj = std::make_shared<MyClass>(); 
  12. // ...
  13. // myobj is automatically freed for you whenever it is no longer used.

std::arraystd::vector代替C风格的数组

这两种方法都保证了对象的连续内存布局,并且可以(而且应该)完全取代C风格数组,另外这也是不使用裸指针的诸多原因之一。

另外,避免使用```std::shared_ptr```保存数组[4]

使用异常

返回值(例如boost::optional),可以被忽略,如果不检查,可能会导致崩溃或内存错误,而异常不能被忽略。另一方面,异常可以被捕获和处理。可能异常会一直上升到应用程序的最高层级被捕获、记录到日志中,并触发应用自动重启。

C++的设计者之一Stroustrup谈论过这个话题: Why use exceptions?[5]

用C++风格的类型转换,而不是C风格的类型转换

用C++风格的强制类型转换(static_cast<>dynamic_cast<>,…)代替C风格的强制类型转换,C++风格的强制转换允许更多的编译器检查,而且相当安全。


   
  1. // Bad Idea
  2. double x = getX();
  3. int i = (int) x;
  4. // Not a Bad Idea
  5. int i = static_cast<int>(x);

此外,C++类型转换风格更为显式,对搜索更为友好。

但如果需要将double类型转换为int类型,请考虑重构程序逻辑(例如,对溢出和下溢进行额外检查)。避免出现测量了3次,然后切割0.9999999999981次这种情况。

不要定义可变参数函数(variadic function)

可变参数函数可以接受数量可变的参数,最著名的例子可能是printf()。虽然可以定义此类函数,但可能存在安全风险。可变参数函数的使用不是类型安全的,错误的输入参数可能导致程序以未定义的行为终止。这种未定义的行为可能会导致安全问题。如果使用支持C++1的编译器,那么可以使用可变参数模板。

参考: It is technically possible to make typesafe C-style variadic functions with some compilers[6]

额外资源

David Wheeler的《How to Prevent The Next Heartbleed[7]》一书很好的分析了代码安全的现状以及如何确保代码安全。

微信公众号:DeepNoMind

参考资料

[1]

C++ Best Practises: https://lefticus.gitbooks.io/cpp-best-practices/content/

[2]

John Carmack对const的评论: http://kotaku.com/454293019

[3]

内存错误和泄漏风险: http://blog2.emptycrate.com/content/nobody-understands-c-part-6-are-you-still-using-pointers

[4]

避免使用std::shared_ptr保存数组: http://stackoverflow.com/questions/3266443/can-you-use-a-shared-ptr-for-raii-of-c-style-arrays

[5]

Why use exceptions?: https://www.stroustrup.com/bs_faq2.html#exceptions-why

[6]

It is technically possible to make typesafe C-style variadic functions with some compilers: https://github.com/lefticus/cppbestpractices/issues/53

[7]

How to Prevent The Next Heartbleed: http://www.dwheeler.com/essays/heartbleed.html

- END -

点击阅读原文加入知识星球

文章来源: blog.csdn.net,作者:程序员编程指南,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_41055260/article/details/127099690

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。