突破编程_C++_高级教程(异常处理的高级特性)

1 异常处理的高级特性

1.1 异常处理的嵌套

在 C++ 中,异常处理是通过 try, catch, throw 关键字来实现的。当在一个 try 块中抛出一个异常时,该异常会被最近的匹配的 catch 块捕获。如果在当前的 try 块或其嵌套的 try 块中没有匹配的 catch 块,那么异常会继续向外层的作用域传播,直到找到匹配的 catch 块或到达程序的顶层。

嵌套异常处理是指在一个 try 块内部有另一个(或多个)try 块。这种情况下,内部的 try 块会首先检查其内部的 catch 块来捕获异常,如果没有找到匹配的 catch 块,异常会传播到外部的 try 块,并检查其 catch 块。

如下为样例代码:

#include <iostream>  
#include <stdexcept>  int main() 
{try {try {// 尝试执行一些可能会抛出异常的代码  throw std::runtime_error("internal error");}catch (const std::runtime_error& e) {// 捕获内部异常并处理  std::cout << "internal exception capture: " << e.what() << std::endl;}// 继续执行其他代码  throw std::runtime_error("external error");}catch (const std::runtime_error& e) {// 捕获外部异常并处理  std::cout << "external exception capture: " << e.what() << std::endl;}return 0;
}

上面代码的输出为:

internal exception capture: internal error
external exception capture: external error

在上面代码中,内部的 try 块首先尝试执行可能会抛出异常的代码,并捕获了一个 std::runtime_error 异常。然后它打印了异常信息并继续执行。随后,外部的 try 块也抛出了一个 std::runtime_error 异常,这个异常由外部的 catch块 捕获并打印了信息。

注意:如果外部 try 块中的代码再次抛出了与内部 catch 块相同的异常类型,那么更外部 catch 块仍然会捕获这个异常,即使内部 try 块已经处理过同类型的异常。这是因为每个 try 块都是独立的,它们各自负责自己的异常处理。

嵌套异常处理在构建健壮的错误处理机制时非常有用,特别是当需要在不同层次的代码中处理不同类型的异常时。然而,过度使用嵌套异常处理可能会使代码变得复杂且难以阅读和维护,因此应该谨慎使用。

1.2 异常传播与函数返回

在 C++ 中,当函数抛出一个异常时,该函数的正常返回流程会被中断。这意味着,如果异常在函数内部被抛出且没有被该函数的 catch 块捕获,那么该函数不会执行其后的任何代码,包括任何返回语句。这种情况下,函数的返回值是不确定的,因为它实际上并没有执行返回操作。

当异常被抛出时,系统会开始寻找匹配的 catch 块。这个过程称为异常传播(exception propagation)。它会从抛出异常的点开始,逐级向上检查函数调用栈中的每个 catch 块,直到找到一个匹配的 catch 块为止。如果找不到匹配的 catch 块,程序将调用 std::terminate 并终止执行。

由于异常中断了函数的正常返回流程,因此函数可能没有机会执行任何清理工作,比如释放资源或回滚事务。为了解决这个问题,可以使用析构函数和 RAII(Resource Acquisition Is Initialization)技术。当异常被抛出时,能够保证会按照对象构造的相反顺序调用所有已构造的局部对象的析构函数。这确保了即使在异常发生的情况下,资源也能得到正确的释放。

此外,如果函数抛出了一个异常且没有被捕获,那么该函数实际上并没有返回任何值。对于非 void 返回类型的函数,这意味着调用该函数的代码将得到一个未定义的值。因此,在设计函数时,应该考虑到异常可能的情况,并确保在异常发生时,函数不会返回未定义的值或导致程序状态的不一致。

如下是一个没有正确处理异常覆盖返回的例子:

#include <iostream>  
#include <stdexcept>  int divide(int a, int b) 
{if (b == 0) {throw std::invalid_argument("Division by zero is not allowed.");}return a / b;
}int main() 
{int a = 1;int b = 0;try {int res = divide(a, b);std::cout << "The result is: " << res << std::endl;}catch (const std::exception& e) {std::cerr << "Exception caught: " << e.what() << std::endl;}// 后续业务逻辑  std::cout << "Continuing with other code..." << std::endl;return 0;
}

在上面代码中,divide 函数用于执行除法操作。如果除数为零,它会抛出一个 std::invalid_argument 异常。在 main 函数中,调用了 divide 函数,并捕获可能抛出的异常。

但是 divide 函数并没有正确处理异常覆盖返回,这可能会出现以下问题:

未定义的返回值: 如果 divide 函数抛出异常且没有被捕获,它将不会返回任何值。在上面的例子中,由于我们使用了 try-catch 块来捕获异常,所以 divide 函数实际上会返回一个值(在异常被抛出的情况下,这个值是未定义的)。但是,如果我们没有在 main 函数中使用 try-catch 块,那么 main 函数将接收到一个未定义的返回值。

后续代码的执行: 在上面代码中,即使捕获了异常并打印了错误消息,程序仍会继续执行 main 函数中的后续代码,输出 “Continuing with other code…”。这可能是因为程序员认为,即使发生异常,程序的其他部分仍然可以安全地执行。然而,这取决于具体的应用场景,有时在捕获异常后继续执行后续代码可能不是安全的做法。

资源泄露和状态不一致: 如果 divide 函数在执行除法之前分配了某些资源(如动态内存),那么在异常被抛出时,这些资源可能不会被正确释放,导致资源泄露。此外,如果函数的执行修改了某些全局状态或外部数据结构,那么在异常被抛出后,这些状态可能会保持在一个不一致的状态。

为了避免这种情况,可以采取以下措施:

确保函数在所有执行路径上都有明确的返回值: 这可以通过在函数的末尾添加一个默认的返回语句来实现,以确保即使发生异常,函数也能返回一个有效的值。

使用 noexcept 关键字来标记不会抛出异常的函数: 这告诉编译器该函数是异常安全的,并且如果该函数抛出了异常,程序将调用 std::terminate 并终止执行。这有助于在编译时捕获可能的异常问题。

在函数的catch块中处理异常,并确保在异常处理完毕后返回一个有效的值: 这样,即使发生了异常,函数也能返回一个定义良好的值,而不是一个未定义的值。

1.3 异常安全:如何编写不泄漏资源的代码

在 C++ 中,异常安全(Exception Safety)是一个重要的编程概念,它指的是在异常发生时,程序能够保持其内部状态的一致性,不会出现资源泄露或数据损坏的情况。为了编写不泄露资源的代码,我们需要采取一些措施来确保异常安全。

以下是实现 C++ 异常安全的一些关键步骤:

使用资源获取即初始化(RAII):
RAII 是一种将资源管理与对象生命周期紧密结合的技术。通过创建对象来管理资源(如内存、文件句柄、数据库连接等),并在对象的析构函数中释放这些资源,可以确保在异常发生时资源得到正确释放。

避免裸指针和手动内存管理:
尽可能使用智能指针(如std::unique_ptr,std::shared_ptr)来管理动态分配的内存。智能指针会在适当的时候自动释放内存,从而减少了内存泄露的风险。

异常安全保证:
函数可以提供不同的异常安全保证等级:
基本保证(Basic Guarantee):在抛出异常后,程序仍然保持有效状态,但可能回滚到操作开始前的状态。
强保证(Strong Guarantee):在抛出异常后,程序保持有效状态,并且不会执行任何部分操作。
无泄露保证(No-Leak Guarantee):即使在抛出异常的情况下,也不会泄露任何资源。

使用拷贝构造函数和赋值操作符的异常安全性:
当实现类的拷贝构造函数和赋值操作符时,要确保它们在异常发生时不会泄露资源。这通常意味着在函数体内使用局部对象来管理资源,并在成功完成操作后再将它们赋值给目标对象。

使用 noexcept 关键字:
如果函数不会抛出异常,可以使用 noexcept 关键字进行标记。这告诉编译器该函数是异常安全的,并允许编译器进行某些优化。

仔细管理全局和静态资源:
全局和静态对象的构造函数和析构函数在程序的生命周期中只执行一次。如果这些构造函数或析构函数抛出异常,可能会导致程序状态不一致。因此,需要确保这些函数是异常安全的。

测试和审查代码:
编写单元测试和集成测试来验证代码的异常安全性。此外,进行代码审查以确保遵循了良好的异常安全实践。

2 异常与资源管理

2.1 使用 RAII(资源获取即初始化)原则

在 C++ 中,RAII(资源获取即初始化)原则是一种强大的技术,用于管理资源并确保在异常发生时不会发生资源泄露。RAII 原则主张将资源的生命周期与对象的生命周期绑定在一起,从而确保当对象被销毁时,其管理的资源也会被适当地释放。

使用 RAII 原则首先需要创建一个类来管理特定的资源。这个类通常会在构造函数中获取(或初始化)资源,并在析构函数中释放资源。这样,当对象离开其作用域或被销毁时,资源将被自动释放:

#include <iostream>  
#include <stdexcept>  struct MyResource {};class ResourceHolder 
{
public:ResourceHolder() {// 获取或初始化资源  resource = allocate_resource();}~ResourceHolder() {// 释放资源  release_resource(resource);}// 禁止拷贝构造函数和赋值操作符  ResourceHolder(const ResourceHolder&) = delete;ResourceHolder& operator=(const ResourceHolder&) = delete;private:MyResource* resource;MyResource* allocate_resource() {// 分配或初始化资源的代码  }void release_resource(MyResource* r) {// 释放资源的代码  delete r;}
};

之后,在可能会抛出异常的函数中,使用资源管理类来管理资源。由于资源管理类的析构函数会在对象销毁时自动调用,因此即使在异常发生时,资源也会被正确释放。

void errorThrow() 
{  ResourceHolder holder;  try {  // 可能抛出异常的代码  } catch (...) {  // 异常处理代码  }  // 当holder离开作用域时,其析构函数将自动释放资源  
}

上面的函数的定义说明在其内部使用 RAII 对象来管理资源,即使发生异常,资源也能得到正确释放。

2.2 使用智能指针管理资源

在 C++ 中,智能指针是一种非常有用的工具,用于自动管理动态分配的内存资源,从而避免内存泄露。智能指针是 RAII(资源获取即初始化)原则的一个实践,它们通过在析构函数中自动释放资源来确保资源的正确管理。在异常处理中,智能指针可以确保即使在异常发生时,资源也能被正确释放。

C++11 及其后续版本提供了几种智能指针,包括 std::unique_ptr、std::shared_ptr、std::weak_ptr 等。这些智能指针都有各自的用途和特性。

(1)使用 std::unique_ptr 管理单个资源:
std::unique_ptr是一种独占所有权的智能指针,它负责删除它所指向的对象。当std::unique_ptr离开其作用域或被重置时,它所指向的对象将被自动删除。
如下为样例代码:

#include <iostream>  
#include <stdexcept>  
#include <memory>  void processData() 
{std::unique_ptr<int[]> datas(new int[100]); // 分配动态数组  try {// 使用data指向的资源进行操作  // ...  }catch (...) {// 异常处理  // ...  }// 当 datas 离开作用域时,它会自动删除所指向的数组  
}

(2)使用 std::shared_ptr 管理共享资源:
std::shared_ptr允许多个智能指针共享同一个对象的所有权。当最后一个std::shared_ptr被销毁或重置时,它负责删除所指向的对象。
如下为样例代码:

#include <iostream>  
#include <stdexcept>  
#include <memory>  struct MyData {};void shareData() 
{std::shared_ptr<MyData> data = std::make_shared<MyData>(); // 使用std::make_shared分配对象  try {// 使用data指向的资源进行操作  // ...  }catch (...) {// 异常处理  // ...  }// 当 data 离开作用域时,如果没有其他 shared_ptr 指向同一个对象,则对象会被自动删除  
}

3 异常处理在实际项目中的应用

3.1 错误处理与异常处理的对比和选择

在 C++ 中,错误处理和异常处理是两种不同的机制,用于处理程序运行时的错误和异常情况。虽然它们有一些重叠之处,但它们在目的、用法和适用场景上有所不同。下面是对错误处理和异常处理的对比和选择的一些指导原则:

错误处理(Error Handling)

(1)目的:
错误处理主要用于检测并处理可预见的错误条件,这些条件通常是程序逻辑的一部分,并且可以通过返回值或错误码来指示。

(2)用法:

  • 使用函数返回值或错误码来指示操作是否成功。
  • 在调用函数后检查返回值或错误码,并根据需要进行错误处理。
  • 可能需要手动释放资源或回滚操作,以处理错误。

如下为一个错误处理的样例代码:

#include <iostream>  // 定义一个错误码枚举  
enum ErrorCode 
{SUCCESS = 0,ERROR_INVALID_INPUT,ERROR_OUT_OF_RANGE,// 其他错误码...  
};// 一个可能抛出错误的函数  
ErrorCode divide(int a, int b, int& res)
{if (b == 0) {// 除数为零,返回错误码  return ERROR_INVALID_INPUT;}res = a / b;return SUCCESS; // 操作成功,返回成功码  
}int main() 
{int a = 1;int b = 0;int res = 0;// 调用可能抛出错误的函数  ErrorCode errorCode = divide(a, b, res);// 检查错误码并进行处理  if (errorCode != SUCCESS) {std::cout << "Calculation error, error code is : " << errorCode << std::endl;// 在这里可以执行其他错误处理逻辑,如记录日志、释放资源等  return 1; // 返回错误状态码  }std::cout << "Result: " << res << std::endl;return 0; // 返回成功状态码  
}

上面代码的输出为:

Calculation error, error code is : 1

在上面代码中,定义了一个 ErrorCode 枚举来表示不同的错误码。divide 函数执行除法操作,并在除数为零时返回一个错误码。之后在 main 函数中调用 divide 函数并检查返回的错误码。如果错误码不是 SUCCESS ,则输出错误信息并返回错误状态码。否则,输出计算结果并返回成功状态码。
这种错误处理方式可以明确指示操作是否成功,并在出现错误时采取适当的措施。它提供了一种简单而有效的方式来处理可预见的错误条件,同时避免了异常处理机制的开销。

(3)适用场景:

  • 当错误是预期之内的,并且可以通过返回值或错误码来明确指示时。
  • 在性能关键的代码中,需要避免异常处理带来的性能开销。
  • 当错误处理逻辑是程序逻辑的一部分,并且需要明确控制错误处理流程时。

异常处理(Exception Handling)

(1)目的:

  • 异常处理用于处理意外或异常的情况,这些情况通常表明程序遇到了不能通过正常逻辑处理的问题。

(2)用法:

  • 使用try-catch块来捕获可能抛出异常的代码段。
  • 在catch块中处理异常,可能包括记录错误、释放资源或采取其他恢复措施。
  • 可以通过异常规格(exception specifications)来指定函数可能抛出的异常类型。

(3)适用场景:

  • 当遇到意外情况或错误时,需要中断正常流程并采取特殊措施。
  • 当错误处理逻辑与常规程序逻辑分离,以提高代码可读性和可维护性时。
  • 在需要传播错误信息到上层调用者时,异常处理提供了一种简洁的机制。

对比和选择

(1)性能:

  • 错误处理通常比异常处理具有更好的性能,因为它避免了异常处理机制的开销。
  • 在性能关键的代码段中,可以考虑选择错误处理而不是异常处理。

(2)可读性和维护性:

  • 异常处理通常提供更好的错误传播和分离错误处理逻辑的机制,这有助于提高代码的可读性和可维护性。
  • 错误处理可能需要手动检查错误码,这可能会使代码变得冗长和复杂。

(3)错误处理策略:

  • 如果程序需要处理可预见的错误条件,并且错误处理逻辑是程序逻辑的一部分,那么错误处理可能是一个更好的选择。
  • 如果程序需要处理意外情况,或者需要一种简洁的机制来传播错误信息,那么异常处理可能更适合。

总体而言,在选择错误处理或异常处理时,应该考虑程序的特定需求、性能要求、代码可读性和可维护性等因素。在某些情况下,可能会结合使用两种机制,以充分利用它们的优势。

3.2 异常处理在大型项目中的策略

在大型 C++ 项目中,异常处理策略是非常重要的,因为它关乎到代码的健壮性、可维护性和性能。以下是一些建议的策略和实践:

(1)避免过度使用异常:

  • 异常应当用于处理错误条件,而不是常规的控制流程。过度使用异常可能导致性能下降和难以调试的代码。
  • 在性能关键的部分,考虑使用错误码或返回值来指示错误,而不是抛出异常。

(2)确保资源在异常时得到释放:

  • 使用智能指针(如std::unique_ptr、std::shared_ptr)和 RAII(资源获取即初始化)原则来自动管理资源。
  • 在可能抛出异常的代码块中,确保资源的初始化发生在可能抛出异常的语句之前,这样即使发生异常,资源的析构函数也会得到调用。

(3)设计清晰的异常层次结构:

  • 创建自定义异常类,这些类继承自 std::exception 或其他自定义基类。
  • 不同的异常类型应代表不同的错误条件,并且应该有一个清晰的继承层次结构,以便进行异常类型的识别和处理。

(4)在合适的地方捕获异常:

  • 只在能够处理异常的层级上捕获异常。不要在底层代码中捕获所有异常,而是允许它们冒泡到能够采取适当措施的上层代码。
  • 使用多个 catch 块来捕获不同类型的异常,并提供不同的处理方式。

(5)记录异常信息:

  • 在捕获异常时,记录有关异常的详细信息,包括异常类型、消息和堆栈跟踪。这有助于后续的调试和问题诊断。

(6)保持异常处理的简洁性:

  • 避免在 catch 块中进行复杂的操作,尤其是可能抛出异常的操作。这可能导致异常处理逻辑的混乱和难以维护。

(7)测试异常处理代码:

  • 编写测试用例来验证异常处理代码的正确性和健壮性。确保在多种错误条件下,异常能够正确地被抛出和捕获。

(8)文档化异常行为:

  • 在函数和类的文档中,明确指出可能抛出的异常类型及其含义。这有助于其他开发人员理解和使用你的代码。

(9)避免在析构函数中抛出异常:

  • 析构函数应该避免抛出异常,因为当析构函数抛出异常时,C++ 标准并不保证会调用已经构造但尚未析构的对象的其他成员的析构函数。这可能导致资源泄露和其他问题。

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

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

相关文章

机器学习提升秘籍:Scikit-learn学习网站全攻略!

介绍&#xff1a;是一个开源的Python机器学习库&#xff0c;它提供了一整套用于数据挖掘和数据分析的工具&#xff0c;包括各种分类、回归、聚类和降维算法以及模型评估、选择和数据预处理等功能。以下是关于Scikit-learn的一些详细介绍&#xff1a; 算法覆盖广泛&#xff1a;S…

【Java基础】Java字符切割工具详解与使用(1)

在Java开发中&#xff0c;字符串操作是一项常见而重要的任务&#xff0c;而字符切割就是其中一个常用的操作之一。本篇博客将深入探讨Java中字符切割的工具&#xff0c;介绍如何使用这些工具&#xff0c;评估它们的性能&#xff0c;并指出可能遇到的一些坑。 1、字符串切割方法…

Mendix 开发实践指南|Mendix的核心概念

在当今快速变化的技术环境中&#xff0c;Mendix平台以模型驱动开发方法&#xff0c;重新定义了应用程序的构建过程。本章内容&#xff0c;将深入探讨Mendix的几大核心概念&#xff1a;模型驱动开发、微流、纳流 、 实体模型和页面&#xff0c;旨在帮助我们全面理解Mendix平台的…

java之Bean对象

1. 什么是Bean&#xff1f; Bean被实例化的&#xff0c;是被Spring框架所管理的Java对象。 Spring容器会自动完成Bean的实例化。将所创建的的Bean自动注入到Ioc容器中以供调用。 spring框架中 IOC容器中管理的对象就是Bean对象 2. 第三方bean Bean 因为第三方bean&#xff0…

《The Art of InnoDB》第二部分|第4章:深入结构-磁盘结构-undo log

4.4 undo log 目录 4.4 undo log 4.4.1 undo log 介绍 4.4.2 undo log 配置 4.4.3 undo log file 结构 4.4.4 undo log mvcc 实现 4.4.5 undo log rollback 实现 4.5.6 undo log 崩溃恢复 4.5.7 小结

​LeetCode解法汇总2476. 二叉搜索树最近节点查询

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; GitHub - September26/java-algorithms: 算法题汇总&#xff0c;包含牛客&#xff0c;leetCode&#xff0c;lintCode等网站题目的解法和代码&#xff0c;以及完整的mode类&#…

模拟实现qsort函数:冒泡排序详解

上一篇qsort函数详解&#xff1a;深入了解C语言的qsort函数&#xff1a;原理及相关知识-CSDN博客 目录 一、冒泡排序的原理 二、模拟实现qsort函数 在C语言中&#xff0c;标准库提供了qsort函数用于对数组进行排序。qsort函数是一个通用的排序函数&#xff0c;可以根据用户提…

Java WEB面试系列-03

1. Java 中如何获取 ServletContext 实例? 1、javax.servlet.Filter中直接获取 ServletContext context = config.getServletContext();2、HttpServlet中直接获取 this.getServletContext()3、在其他方法中通过HttpRequest获得 request.getSession().getServletContext();…

如何在Win系统从零开始搭建Z-blog网站,并将本地博客发布到公网可访问

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕…

每日OJ题_分治归并②_LCR 170. 交易逆序对的总数

目录 力扣LCR 170. 交易逆序对的总数 解析代码1 解析代码2 力扣LCR 170. 交易逆序对的总数 LCR 170. 交易逆序对的总数 难度 困难 在股票交易中&#xff0c;如果前一天的股价高于后一天的股价&#xff0c;则可以认为存在一个「交易逆序对」。请设计一个程序&#xff0c;输…

条款02:尽量以const,enum,inline代替#define

目录 1.const定义常量 2.cosnt修饰指针 3.class的专属常量 4.the enum hack 5.用inline代替#define宏函数 1.const定义常量 #define B 1.63 const int B 1.63 原因&#xff1a; 1.#define是在预处理阶段&#xff0c;直接将代码中的B替换为1.63会产生多份&#xff0c;而…

uniapp如何实现关闭前面指定数目页面

需求 &#xff1a; 路由从 页面A -> 页面B-> 页面C-> 页面D 我希望在 页面C跳到页面CD 后 在页面D 中 点击返回&#xff08;物理键或是代码返回&#xff09;&#xff0c;直接返回到A,而不是页面C 所以我需要把BC页面给销毁掉 以下是我的实现思路&#xff0c;在h…

Linux系统编程之线程互斥锁的使用方法

文章目录 一、Linux上线程开发互斥锁概要二、创建及销毁互斥锁2.1 示例&#xff1a;主线程等待两个线程退出&#xff0c;1线程和2线程打印信息 三、互斥量的初始化问题 一、Linux上线程开发互斥锁概要 互斥量&#xff08;mutex&#xff09;从本质上来说是一把锁&#xff0c;在…

Stable Diffusion 模型分享:yayoi_mix(日本美女、真实、照片)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五 下载地址 模型介绍 yayoi_mix 是一个现实模型&#xff0c;是一个针对日本女性训练及合并的模型。 条目内容类型大模型基础模型SD…

前后端项目宝塔linux部署(springboot,vue,python)

宝塔linux安装就省略了&#xff0c;网上一堆 1.部署后端 1.首先把自己项目里面打包好的的jar包上传到服务器随便一个地方&#xff0c;我这里就上传到www/wwwroot下面了&#xff0c;宝塔的文件页面可以很便携上传 2.然后到下面这个页面 选那个java环境管理装个jdk&#xff…

全网首个GDB移植手册【Howto:Porting the GUN Debugger】翻译

Howto:Porting the GUN Debugger ✍【作者】&#xff1a;电子科大不知名程序员 &#x1f4e3;【说明】&#xff1a;本文是自己在搭建mcore架构GDB时的参考的手册&#xff0c;具有很强的学习指导性&#xff0c;因原文档&#xff08;链接&#xff1a;https://www.embecosm.com/a…

企业文件图纸加密有哪些?图纸文件加密防泄密软件如何选?

在现在的市场发展中&#xff0c;对于企业的图纸文件安全问题越来越重视&#xff0c;如设计图纸&#xff0c;重要文件等&#xff0c;一旦泄漏就会给企业造成巨大的经济损失。所以对企业管理者来讲&#xff0c;如何才能选择一款好用的适合本企业的图纸文件加密软件是非常重要的&a…

【常用的 SVN 命令及简要示例】

常用的 SVN 命令及简要示例&#xff0c;供您参考&#xff1a; 检出代码: svn checkout https://svn.example.com/project/trunk提交修改: svn commit -m "Commit message"更新代码: svn update创建分支: svn copy https://svn.example.com/project/trunk \https://sv…

网络编程难点之select、poll与epoll详解

前言 为什么需要I/O多路复用技术&#xff1f; 首先&#xff0c;I/O多路复用技术主要被应用在需要高性能的网络服务器程序中。 高性能网络服务器程序需要做的事情就是供多个客户端同时进行连接并处理客户端传送过来的数据请求&#xff1a; 对于这种情况&#xff0c;很多人自然…

【招生】江西师范大学—地质灾害研究团队—地理与环境学院/鄱阳湖流域实验室

【招生】江西师范大学—地质灾害研究团队—地理与环境学院/鄱阳湖流域实验室 研究方向&#xff1a;InSAR、极化SAR、GNSS、地球物理、GIS 招生专业&#xff1a;GIS、人文地理 学院地址&#xff1a;江西南昌江西师范大学