浅谈C++中的防御性编程

目录

1.什么是防御性编程

2.防御性编程技巧

2.1.采用良好的编码风格

2.2.合理使用assert

2.3.检查函数参数

2.4.使用异常处理

2.5.避免裸指针

2.6.资源管理

2.7.最小化使用全局变量

2.8.封装和模块化

2.9.避免使用宏

2.10.初始化所有变量

2.11.使用范围枚举

2.12.防止数组越界

2.13.使用标准库和智能算法

2.14.线程安全

2.15.代码审查和测试

3.实践中的防御性编程

4.总结


1.什么是防御性编程

        防御性编程是一种编程实践,旨在提高软件的健壮性和可靠性,减少运行时错误。在C++中,这尤其重要,因为C++是一种允许进行低级内存操作和指针操作的语言,这使得它容易出错。

        顾名思义,防御性编程是一种细致、谨慎的编程方法。为了开发可靠的软件,我们要设计系统中的每个组件,以使其尽可能的”保护”自己。我们通过明确地在代码中对设想进行检查,这是一种努力,防止我们的代码以将会展现错误行为的方式被调用。

2.防御性编程技巧

2.1.采用良好的编码风格

1) const关键字

C/C++中const关键字用法总结_返回对象或复合类型的常量引用-CSDN博客

        关键字const可以给读你代码的人传达非常有用的信息。例如,在函数的形参前添加const关键字意味着这个参数在函数体内不会被修改,属于输入参数。

        同时,合理地使用关键字const可以使编译器很自然的保护那些不希望被修改的参数,防止其被无意的代码修改,减少bug的出现。

2) volatile关键字

        在一些并行设备的硬件寄存器(如状态寄存器),中断服务子程序中会访问到的全局变量以及多线程应用中被几个任务共享的变量前使用volatile关键字来防止编译优化。

3) static关键字

C/C++中static关键字用法总结_c++ static 关键词用法-CSDN博客

        函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值。

        在模块内的static全局变量可以被模块内的所有函数访问,但不能被模块外其它函数访问。

        在模块内的static函数只可能被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内。

4) 位操作运算中,尽可能使用<<、 >>、 &、|等运算符,尽可能少使用/、%、*运算符。

5) 变量和函数的命名要有意义,并且尽可能做到一个函数只做一件事情。

6) 多采用面向对象的思想来编写代码。

7) 在投入到编码工作之前,先考虑大体的设计方案,这也非常关键。

2.2.合理使用assert

C++之assert惯用法_c++ assert-CSDN博客

        断言(assert)是一种调试辅助工具,用于在代码中设置检查点。如果条件为真,程序可以继续执行;如果条件为假,程序将显示错误消息并终止。这有助于在开发阶段捕获错误。

        在日常编程过程中,可以在无法预知的逻辑当中增加assert判断,很容易发现程序中的逻辑错误。比如下面的代码:

//示例1,检查输入参数的合法性
void func(int value) {assert(value >= 0 && value <= 10); // 假设value应该在0到10之间// 处理value
}//示例2
using dealWithFunc = std::function<void(const void*, int)>;
std::map<int, dealWithFunc> taskCmds;bool func1(int type){auto it = taskCmds.find(type);if (it == taskCmds.end()){assert(false); //没有处理这个type的逻辑, 逻辑一旦走到这里,就知道某个type的事件没有处理return false;}。。。return true;
}

2.3.检查函数参数

函数应检查其参数的有效性,并在接收到无效参数时采取适当的行动,如返回错误代码或抛出异常。

void process(const char* data, size_t length) {if (data == nullptr) {throw std::invalid_argument("data pointer is null");}if (length == 0) {throw std::invalid_argument("length cannot be zero");}// 处理数据
}

2.4.使用异常处理

在C++中,异常处理是一个强大的工具,可以用于捕获和处理运行时错误。通过使用try-catch块,可以优雅地处理异常情况,避免程序崩溃。

try {// 可能抛出异常的代码
} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;
}

2.5.避免裸指针

尽可能使用智能指针(如std::unique_ptrstd::shared_ptr)代替裸指针,以减少内存泄漏和悬挂指针的风险。

std::unique_ptr<MyClass> ptr(new MyClass());
ptr->doSomething();

2.6.资源管理

C++惯用法之RAII思想: 资源管理_raii 思想-CSDN博客

在C++中,资源管理(如内存、文件句柄等)是一个重要的问题。RAII(Resource Acquisition Is Initialization)是一种常用的资源管理策略,通过构造函数获取资源,析构函数释放资源,确保资源的正确释放。

class FileWrapper {
public:FileWrapper(const std::string& filename) {file = std::fopen(filename.c_str(), "r");if (!file) {throw std::runtime_error("Unable to open file");}}~FileWrapper() {if (file) {std::fclose(file);}}private:FILE* file;
};

2.7.最小化使用全局变量

全局变量在程序中随时可以被修改,容易引发难以调试的错误。尽量使用局部变量和参数传递,保持代码的模块化和可维护性。

class MyClass {
public:void doSomething() {int localVar = 0;helperFunction(localVar);}private:void helperFunction(int value) {// 使用局部变量进行处理}
};

2.8.封装和模块化

封装是面向对象编程的基本原则之一。通过封装数据和方法,可以减少模块之间的耦合,提高代码的可维护性和可扩展性。

1) 封装数据和方法:通过封装数据和方法,可以减少模块之间的耦合,提高代码的可维护性和可扩展性。

2) 使用局部变量和参数传递:尽量使用局部变量和参数传递,避免使用全局变量,因为全局变量在程序中随时可以被修改,容易引发难以调试的错误。

class MyClass {
public:void setValue(int value) {if (value >= 0) {this->value = value;} else {throw std::invalid_argument("value cannot be negative");}}int getValue() const {return value;}
private:int value;
};

2.9.避免使用宏

宏可能导致代码难以理解和维护。尽量使用常量、内联函数或模板来代替宏。

2.10.初始化所有变量

确保所有变量在使用前都已初始化,以避免未定义行为。

class MyClass {
public:MyClass() : value(0) {}private:int value;
};

2.11.使用范围枚举

使用范围枚举(enum class)代替传统的枚举,以避免枚举值的隐式转换和名称冲突。

enum class Color { Red, Green, Blue };
Color color = Color::Red;

2.12.防止数组越界

在访问数组元素时,确保索引在有效范围内,避免数组越界访问。非常容易搞错的有以下几点:

1)字符串和字符数组的区别,字符串必须是以'\0'结束,字符数组必须带长度。比如:

char name[7] = "1234455"; // "1234455"
char* name = "1234455";   // "1234455\0"

2)数组在函数传递的过程中会退变成指针,所以必须带长度,不然不知道长度

void accessArray(int* arr, size_t size, size_t index) {if (index >= size) {throw std::out_of_range("index out of range");}// 访问arr[index]
}int a[] = {1,44,56,7,8,9,9};
accessArray(a, sizeof(a)/sizeof(a[0]), 5);

2.13.使用标准库和智能算法

C++标准库提供了多种容器(如std::vector、std::map等),它们封装了复杂的数据结构和操作,能有效避免内存泄漏和指针错误。

std::vector<int> vec = {1, 2, 3};
vec.push_back(4);std::sort(vec.begin(), vec.end());

2.14.线程安全

深入理解C++中的锁_c++锁的类型-CSDN博客

在多线程环境中,确保代码的线程安全是至关重要的。使用互斥锁(mutex)、条件变量(condition variable)等同步机制,确保多个线程访问共享资源时不会产生冲突。

std::mutex mtx;
void threadSafeFunction() {std::lock_guard<std::mutex> lock(mtx);// 访问共享资源
}

2.15.代码审查和测试

代码审查

代码审查(Code Review)是一种通过检查源代码来找出并修正错误的系统性方法。它可以帮助开发团队提高代码质量,促进团队成员之间的知识共享,以及增强团队对软件项目的整体理解。代码审查通常关注以下几个方面:

  • 代码质量:检查代码是否遵循了编码规范,是否存在潜在的错误或不合理的设计。
  • 可读性:评估代码是否易于理解,变量、函数和类的命名是否清晰。
  • 性能:分析代码的执行效率,是否存在性能瓶颈。
  • 安全性:检查代码是否存在安全漏洞,如SQL注入、跨站脚本(XSS)等。

测试

测试是验证软件功能、性能和安全性是否符合预期要求的过程。它包括多个层次和类型,如单元测试、集成测试、系统测试和验收测试等。

  • 单元测试:针对软件中的最小可测试单元(如函数或方法)进行的测试。
  • 集成测试:测试软件模块之间的交互,确保它们能够正确地协同工作。
  • 系统测试:将整个软件系统作为一个整体进行测试,验证其是否满足规定的需求。
  • 验收测试:由用户或客户进行的测试,以确认软件是否满足他们的需求和期望。

代码审查与测试的关系

代码审查和测试是相互补充的。代码审查可以在代码提交之前发现并修正错误,而测试则可以在代码运行期间验证其功能和性能。结合使用这两种方法可以显著提高软件的质量和开发效率。

  • 提高代码质量:通过代码审查,可以及早发现并修正潜在的错误和不合理的设计。而测试则可以进一步验证代码的正确性和稳定性。
  • 促进知识共享:代码审查是一个团队成员之间相互学习和交流的过程,有助于提升整个团队的技术水平。测试则可以让团队成员更深入地了解软件的功能和性能。
  • 增强软件安全性:代码审查和测试都可以帮助发现软件中的安全漏洞,并及时进行修复,从而增强软件的安全性。

        综上所述,代码审查和测试是软件开发过程中不可或缺的两个环节。它们相互补充,共同确保软件的质量、稳定性和安全性。在实际开发过程中,应该充分重视并合理运用这两种方法,以提高软件开发效率和软件产品质量。

3.实践中的防御性编程

        在实际开发中,防御性编程不仅仅是一个技术问题,更是一种编码思维和习惯的养成。以下是一些具体的实践建议:

        代码审查定期进行代码审查,发现潜在的错误和问题。通过集体智慧,可以提高代码的质量和健壮性。

        编写单元测试单元测试可以帮助验证代码的正确性,捕获边界条件和异常情况。编写全面的单元测试是防御性编程的重要组成部分。

        持续学习和改进防御性编程是一门需要不断学习和实践的艺术。通过阅读相关书籍、博客和参加技术讨论,可以不断提高自己的防御性编程水平。

4.总结

        通过实施这些防御性编程策略,你可以提高C++代码的质量,减少错误和漏洞,从而创建更可靠、更易于维护的软件,更重要的是减少软件交付的时间,节约开发成本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/41659.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开发必备基础知识【Linux环境变量文件合集】

开发必备基础知识【Linux环境变量文件合集】 在Linux系统中&#xff0c;环境配置文件用于定制用户的Shell环境&#xff0c;包括定义环境变量、设置命令别名、定义启动脚本等。不同的Shell&#xff08;如bash、zsh&#xff09;有着各自对应的配置文件。 .bashrc&#xff1a;每新…

【HTML】-解决页面内容无法选择、复制问题

目录 1、网页内容无法选中 1.1、问题原因 1.2、解决脚本 1.2.1、开启控制台窗口 1.2.2、执行脚本命令 2、内容复制弹出阻止框 2.2、解决脚本 1、网页内容无法选中 1.1、问题原因 今天在访问某一网站平台&#xff0c;需要将内容进行选择、复制时发现不可使用。 在使用…

圆通寄15kg30kg一般多少钱?寄大件物品怎么寄最便宜?

作为一名即将毕业的大学生&#xff0c;搬家成了我和室友们共同的难题。尤其是在寄送大件物品时&#xff0c;如何省钱、如何打包、选择哪家快递公司等问题让我们头疼不已。今天&#xff0c;我就来分享一些寄大件物品的省钱技巧以及打包方法&#xff0c;希望对大家有所帮助。 一…

Ubuntu部署SpringBoot项目

文章目录 Ubuntu部署SpringBoot项目一、JDK1.1 下载1.2 解压JDK配置环境变量1.3 是否安装成功1.4 开机自启1.4.1 修改脚本1.4.2 设置开机自启 二、Redis2.1 下载2.2 解压Redis并安装2.3 开机自启2.3.1 修改脚本2.3.2 设置开机自启 三、Nginx3.1 安装Nginx依赖包3.1.1 prce-deve…

Python酷库之旅-第三方库Pandas(006)

目录 一、用法精讲 10、pandas.DataFrame.to_excel函数 10-1、语法 10-2、参数 10-3、功能 10-4、返回值 10-5、说明 10-6、用法 10-6-1、数据准备 10-6-2、代码示例 10-6-3、结果输出 11、pandas.ExcelFile类 11-1、语法 11-2、参数 11-3、功能 11-4、返回值 …

BUUCTF - Basic

文章目录 1. Linux Labs 【SSH连接漏洞】2. BUU LFI COURSE【文件包含漏洞】3. BUU BRUTE【暴力破解用户名密码】4. BUU SQL COURSE【SQL注入-当前数据库】5. Upload-Labs-Linux 1【文件上传漏洞】7. Buu Upload Course 1【文件上传包含漏洞】8. sqli-labs 1【SQL注入-服务器上…

MySQL常用高级特性

MySQL作为一种广泛使用的关系型数据库管理系统&#xff0c;除了基本的数据库功能外&#xff0c;还提供了许多高级特性来满足不同应用场景的需求。以下是一些MySQL的常用高级特性及其详细讲解&#xff1a; 1. 存储引擎 功能介绍 MySQL支持多种存储引擎&#xff0c;每种引擎在…

C++线程的使用

C11之前&#xff0c;C语言没有对并发编程提供语言级别的支持&#xff0c;这使得我们在编写可移植的并发程序时&#xff0c;存在诸多的不便。现在C11中增加了线程以及线程相关的类&#xff0c;很方便地支持了并发编程&#xff0c;使得编写的多线程程序的可移植性得到了很大的提高…

使用Java构建物联网应用的最佳实践

使用Java构建物联网应用的最佳实践 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 随着物联网&#xff08;IoT&#xff09;技术的快速发展&#xff0c;越来越…

【FFMPEG基础(一)】解码源码

学习分享 main函数decodetorgb32.h 文件decodetorgb32 .cpp文件 main函数 #include <QApplication> #include "decodetorgb32.h" int main(int argc, char *argv[]) {QApplication a(argc, argv);DecodeToRGB32 toRGB32;int restoRGB32.openVideo("../fi…

01 Web基础与HTTP协议

1.1 Web 基础 本章将介绍 Web 基础知识&#xff0c;包括域名的概念、DNS 原理、静态网页和动态网页的相关知识。 1.1.1.域名概述 1.域名的概念 ip地址不易记忆 2.早期使用host文件解析域名 主机名重复主机维护困难 3.DNS 分布式层次式 4.域名空间结构 根域顶级域 组…

InstantStyle-Plus:风格转移与内容保留在文本到图像的生成

在之前的文章中已经和大家介绍过小红书在风格保持方面的优秀工作InstantID和InstantStyle,感兴趣的小伙伴可以点击下面&#x1f447;链接阅读&#xff5e; 小红书InstantID来了, 一张照片几秒钟就能生成个性化图片, 无缝衔接Stable Diffusion&#xff09; InstantID作者新作&…

国外使用代理IP的安全风险

1. 数据泄露 当使用代理IP时&#xff0c;用户的真实IP地址被隐藏&#xff0c;但仍然存在数据泄露的风险。如果代理服务器没有进行恰当的安全措施&#xff0c;用户的个人信息和访问记录可能会被黑客窃取或监控。 2. 恶意软件 在使用代理IP时&#xff0c;用户可能会在代理服务器…

关于 Mac 系统 .DS_store 文件的起源

原文&#xff1a;Arno - 2006.10.01 &#xff08;前排提醒&#xff1a;可以在 .gitignore 中添加 .DS_Store&#xff0c;否则 git 仓库会存储这个和项目无关的文件。&#xff09; 如果你是 Mac 用户&#xff0c;曾经将文件从 Mac 传输到 Windows&#xff0c;那么可能对 .DS_S…

c++ word转换为pdf

在windows系统下&#xff0c;使用QAxObject效果是最好的 转60多兆的文件速度还是可以的&#xff0c;不建议使用多线程&#xff0c;因为多线程会多次调用转换函数&#xff0c;导致程序一直运行&#xff0c;只有全部转换完成后&#xff0c;程序才能继续向下运行&#xff0c;但是c…

SketchUp + Enscape+ HTC Focus3 VR

1. 硬件: 设备连接 2. 软件: 安装steam steamVR Vive Business streaming 3. 操作: 双方登录steam 账号,然后带上头盔,用手柄在HTC Focus3 安装 串流软件,选择串流软件,在Enscape中选择 VR 模式即可 4.最终效果: SketchUp Enscape HTC Focus 3 VR 实时预览_哔哩哔哩_bi…

云微客短视频矩阵全域营销,更高效的获客引流方式!

在抖音这样一个拥有海量用户和内容的短视频平台上&#xff0c;单一账号往往难以覆盖我们的客户群体&#xff0c;甚至于每天发布四五条视频&#xff0c;所引发的流量也是微乎其微的。在竞争如此激烈的市场环境中&#xff0c;商家企业无不想方设法追求更高效的获客引流方式&#…

SQL Server 查询死锁以及解决死锁的基本知识(图文)

目录 1. 基本知识2. 查看和解锁被锁的表3. 查看和处理数据库堵塞 1. 基本知识 在 SQL Server 中&#xff0c;死锁是指两个或多个进程互相等待对方持有的资源&#xff0c;从而无法继续执行的现象 要解决死锁问题&#xff0c;首先需要识别并分析死锁的发生原因&#xff0c;然后…

Floyd判圈算法——环形链表(C++)

Floyd判圈算法(Floyd Cycle Detection Algorithm)&#xff0c;又称龟兔赛跑算法(Tortoise and Hare Algorithm)&#xff0c;是一个可以在有限状态机、迭代函数或者链表上判断是否存在环&#xff0c;求出该环的起点与长度的算法。 …

什么是断路器模式?Hystrix在其中扮演什么角色?

断路器模式&#xff08;Circuit Breaker Pattern&#xff09; 断路器模式是一种设计模式&#xff0c;它用于在分布式系统中防止级联失败的发生。类比于电路中的断路器&#xff0c;该模式的核心思想是当系统检测到一些调用或响应出现问题&#xff08;如超时、异常等&#xff09…