广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++中的异常处理机制介绍
  • 793
分享到

C++中的异常处理机制介绍

2023-06-17 05:06:25 793人浏览 泡泡鱼
摘要

本篇内容介绍了“c++中的异常处理机制介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!异常处理增强错误恢复能力是提高代码健壮性的最有力的途

本篇内容介绍了“c++中的异常处理机制介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

异常处理

增强错误恢复能力是提高代码健壮性的最有力的途径之一,C语言中采用的错误处理方法被认为是紧耦合的,函数的使用者必须在非常靠近函数调用的地方编 写错误处理代码,这样会使得其变得笨拙和难以使用。C++中引入了异常处理机制,这是C++的主要特征之一,是考虑问题和处理错误的一种更好的方式。使用 错误处理可以带来一些优点,如下:

  • 错误处理代码的编写不再冗长乏味,并且不再和正常的代码混合在一起,程序员只需要编写希望产生的代码,然后在后面某个单独的区段里编写处理错误的嗲吗。多次调用同一个函数,则只需要某个地方编写一次错误处理代码。

  • 错误不能被忽略,如果一个函数必须向调用者发送一次错误信息。它将抛出一个描述这个错误的对象。

传统的错误处理和异常处理

在讨论异常处理之前,我们先谈谈C语言中的传统错误处理方法,这里列举了如下三种:

  • 在函数中返回错误,函数会设置一个全局的错误状态标志。

  • 使用信号来做信号处理系统,在函数中raise信号,通过signal来设置信号处理函数,这种方式耦合度非常高,而且不同的库产生的信号值可能会发生冲突

  • 使用标准C库中的非局部跳转函数 setjmp和longjmp ,这里使用setjmp和longjmp来演示下如何进行错误处理:

#include <iOStream> #include <setjmp.h> jmp_buf static_buf; //用来存放处理器上下文,用于跳转  void do_jmp() {     //do something,simetime occurs a little error     //调用longjmp后,会载入static_buf的处理器信息,然后第二个参数作为返回点的setjmp这个函数的返回值     longjmp(static_buf,10);//10是错误码,根据这个错误码来进行相应的处理 }  int main() {     int ret = 0;     //将处理器信息保存到static_buf中,并返回0,相当于在这里做了一个标记,后面可以跳转过来     if((ret = setjmp(static_buf)) == 0) {         //要执行的代码         do_jmp();     } else {    //出现了错误         if (ret == 10)             std::cout << "a little error" << std::endl;     } }

错误处理方式看起来耦合度不是很高,正常代码和错误处理的代码分离了,处理处理的代码都汇聚在一起了。但是基于这种局部跳转的方式来处理代码,在 C++中却存在很严重的问题,那就是对象不能被析构,局部跳转后不会主动去调用已经实例化对象的析构函数。这将导致内存泄露的问题。下面这个例子充分显示 了这点

#include <iostream> #include <csetjmp>  using namespace std;  class base {     public:         base() {             cout << "base construct func call" << endl;         }         ~base() {             cout << "~base destruct func call" << endl;         } };  jmp_buf static_buf;  void test_base() {     base b;     //do something     longjmp(static_buf,47);//进行了跳转,跳转后会发现b无法析构了 }  int main() {     if(setjmp(static_buf) == 0) {         cout << "deal with some thing" << endl;         test_base();     } else {         cout << "catch a error" << endl;     } }

在上面这段代码中,只有base类的构造函数会被调用,当longjmp发生了跳转后,b这个实例将不会被析构掉,但是执行流已经无法回到这里,b 这个实例将不会被析构。这就是局部跳转用在C++中来处理错误的时候带来的一些问题,在C++中异常则不会有这些问题的存在。那么接下来看看如何定义一个 异常,以及如何抛出一个异常和捕获异常吧.

异常的抛出

class MyError {     const char* const data; public:     MyError(const char* const msg = 0):data(msg)     {         //idle     } };  void do_error() {     throw MyError("something bad happend"); }  int main() {     do_error(); }

上面的例子中,通过throw抛出了一个异常类的实例,这个异常类,可以是任何一个自定义的类,通过实例化传入的参数可以表明发生的错误信息。其实 异常就是一个带有异常信息的类而已。异常被抛出后,需要被捕获,从而可以从错误中进行恢复,那么接下来看看如何去捕获一个异常吧。在上面这个例子中使用抛 出异常的方式来进行错误处理相比与之前使用局部跳转的实现来说,***的不同之处就是异常抛出的代码块中,对象会被析构,称之为堆栈反解.

异常的捕获

C++中通过catch关键字来捕获异常,捕获异常后可以对异常进行处理,这个处理的语句块称为异常处理器。下面是一个简单的捕获异常的例子:

try{     //do something     throw string("this is exception"); } catch(const string& e) {     cout << "catch a exception " << e << endl; }

catch有点像函数,可以有一个参数,throw抛出的异常对象,将会作为参数传递给匹配到到catch,然后进入异常处理器,上面的代码仅仅是 展示了抛出一种异常的情况,加入try语句块中有可能会抛出多种异常的,那么该如何处理呢,这里是可以接多个catch语句块的,这将导致引入另外一个问 题,那就是如何进行匹配。

异常的匹配

异常的匹配我认为是符合函数参数匹配的原则的,但是又有些不同,函数匹配的时候存在类型转换,但是异常则不然,在匹配过程中不会做类型的转换,下面的例子说明了这个事实:

#include <iostream>  using namespace std; int main() {     try{          throw 'a';     }catch(int a) {         cout << "int" << endl;     }catch(char c) {         cout << "char" << endl;     } }

上面的代码的输出结果是char,因为抛出的异常类型就是char,所以就匹配到了第二个异常处理器。可以发现在匹配过程中没有发生类型的转换。将 char转换为int。尽管异常处理器不做类型转换,但是基类可以匹配到派生类这个在函数和异常匹配中都是有效的,但是需要注意catch的形参需要是引 用类型或者是指针类型,否则会 导致切割派生类这个问题。

//基类 class Base{     public:         Base(string msg):m_msg(msg)         {         }         virtual void what(){             cout << m_msg << endl;         }     void test()     {         cout << "I am a CBase" << endl;     }     protected:         string m_msg; }; //派生类,重新实现了虚函数 class CBase : public Base {     public:         CBase(string msg):Base(msg)         {          }         void what()         {            cout << "CBase:" << m_msg << endl;         } };  int main() {     try {         //do some thing     //抛出派生类对象         throw CBase("I am a CBase exception");      }catch(Base& e) {  //使用基类可以接收         e.what();     } }

上面的这段代码可以正常的工作,实际上我们日常编写自己的异常处理函数的时候也是通过继承标准异常来实现字节的自定义异常的,但是如果将 Base&换成Base的话,将会导致对象被切割,例如下面这段代码将会编译出错,因为CBase被切割了,导致CBase中的test函数无法 被调用。

try {     //do some thing     throw CBase("I am a CBase exception");  }catch(Base e) {     e.test(); }

到此为此,异常的匹配算是说清楚了,总结一下,异常匹配的时候基本上遵循下面几条规则:

异常匹配除了必须要是严格的类型匹配外,还支持下面几个类型转换.

  • 允许非常量到常量的类型转换,也就是说可以抛出一个非常量类型,然后使用catch捕捉对应的常量类型版本

  • 允许从派生类到基类的类型转换

  • 允许数组被转换为数组指针,允许函数被转换为函数指针

假想一种情况,当我要实现一代代码的时候,希望无论抛出什么类型的异常我都可以捕捉到,目前来说我们只能写上一大堆的catch语句捕获所有可能在 代码中出现的异常来解决这个问题,很显然这样处理起来太过繁琐,幸好C++提供了一种可以捕捉任何异常的机制,可以使用下列代码中的语法。

catch(...) {
    //异常处理器,这里可以捕捉任何异常,带来的问题就是无法或者异常信息
   }

如果你要实现一个函数库,你捕捉了你的函数库中的一些异常,但是你只是记录日志,并不去处理这些异常,处理异常的事情会交给上层调用的代码来处理.对于这样的一个场景C++也提供了支持.

try{     throw Exception("I am a exception");    }catch(...) {     //log the exception     throw; }

通过在catch语句块中加入一个throw,就可以把当前捕获到的异常重新抛出.在异常抛出的那一节中,我在代码中抛出了一个异常,但是我没有使用任何catch语句来捕获我抛出的这个异常,执行上面的程序会出现下面的结果.

terminate called after throwing an instance of 'MyError' Aborted (core dumped)

为什么会出现这样的结果呢?,当我们抛出一个异常的时候,异常会随着函数调用关系,一级一级向上抛出,直到被捕获才会停止,如果最终没有被捕获将会 导致调用terminate函数,上面的输出就是自动调用terminate函数导致的,为了保证更大的灵活性,C++提供了set_terminate 函数可以用来设置自己的terminate函数.设置完成后,抛出的异常如果没有被捕获就会被自定义的terminate函数进行处理.下面是一个使用的 例子:

#include <exception> #include <iostream> #include <cstdlib> using namespace std;  class MyError {     const char* const data; public:     MyError(const char* const msg = 0):data(msg)     {         //idle     } };  void do_error() {     throw MyError("something bad happend"); } //自定义的terminate函数,函数原型需要一致 void terminator() {     cout << "I'll be back" << endl;     exit(0); }  int main() {     //设置自定义的terminate,返回的是原有的terminate函数指针     void (*old_terminate)() = set_terminate(terminator);     do_error(); } 上面的代码会输出I'll be back

到此为此关于异常匹配的我所知道的知识点都已经介绍完毕了,那么接着可以看看下一个话题,异常中的资源清理.

异常中的资源清理

在谈到局部跳转的时候,说到局部调转不会调用对象的析构函数,会导致内存泄露的问题,C++中的异常则不会有这个问题,C++中通过堆栈反解将已经 定义的对象进行析构,但是有一个例外就是构造函数中如果出现了异常,那么这会导致已经分配的资源无法回收,下面是一个构造函数抛出异常的例子:

#include <iostream> #include <string> using namespace std;  class base {     public:         base()         {             cout << "I start to construct" << endl;             if (count == 3) //构造第四个的时候抛出异常                 throw string("I am a error");             count++;         }          ~base()         {             cout << "I will destruct " << endl;         }     private:         static int count; };  int base::count = 0;  int main() {         try{              base test[5];          } catch(...){              cout << "catch some error" << endl;          } } 上面的代码输出结果是: I start to construct I start to construct I start to construct I start to construct I will destruct I will destruct I will destruct catch some error

在上面的代码中构造函数发生了异常,导致对应的析构函数没有执行,因此实际编程过程中应该避免在构造函数中抛出异常,如果没有办法避免,那么一定要 在构造函数中对其进行捕获进行处理.***介绍一个知识点就是函数try语句块,如果main函数可能会抛出异常该怎么捕获?,如果构造函数中的初始化列表 可能会抛出异常该怎么捕获?下面的两个例子说明了函数try语句块的用法:

#include <iostream>  using namespace std;  int main() try {     throw "main"; } catch(const char* msg) {     cout << msg << endl;     return 1; } main函数语句块,可以捕获main函数中抛出的异常. class Base {     public:         Base(int data,string str)try:m_int(data),m_string(str)//对初始化列表中可能会出现的异常也会进行捕捉        {             // some initialize opt        }catch(const char* msg) {              cout << "catch a exception" << msg << endl;        }      private:         int m_int;         string m_string; };  int main() {     Base base(1,"zhangyifei"); }

上面说了很多都是关于异常的使用,如何定义自己的异常,编写异常是否应该遵循一定的标准,在哪里使用异常,异常是否安全等等一系列的问题,下面会一一讨论的.

标准异常

C++标准库给我们提供了一系列的标准异常,这些标准异常都是从exception类派生而来,主要分为两大派生类,一类是 logic_error,另一类则是runtime_error这两个类在stdexcept头文件中,前者主要是描述程序中出现的逻辑错误,例如传递了 无效的参数,后者指的是那些无法预料的事件所造成的错误,例如硬件故障或内存耗尽等,这两者都提供了一个参数类型为std::string的构造函数,这 样就可以将异常信息保存起来,然后通过what成员函数得到异常信息.

#include <stdexcept> #include <iostream> #include <string> using namespace std;  class MyError:public runtime_error { public:     MyError(const string& msg = "") : runtime_error(msg) {}  };  //runtime_error logic_error 两个都是继承自标准异常,带有string构造函数 // int main() {     try {         throw MyError("my message");       }   catch(MyError& x) {         cout << x.what() << endl;        } }

异常规格说明

假设一个项目中使用了一些第三方的库,那么第三方库中的一些函数可能会抛出异常,但是我们不清楚,那么C++提供了一个语法,将一个函数可能会抛出 的异常列出来,这样我们在编写代码的时候参考函数的异常说明即可,但是C++11中这中异常规格说明的方案已经被取消了,所以我不打算过多介绍,通过一个 例子看看其基本用法即可,重点看看C++11中提供的异常说明方案:

#include <exception> #include <iostream> #include <cstdio> #include <cstdlib> using namespace std;  class Up{}; class Fit{}; void g(); //异常规格说明,f函数只能抛出Up 和Fit类型的异常 void f(int i)throw(Up,Fit) {     switch(i) {         case 1: throw Up();         case 2: throw Fit();        }     g(); }  void g() {throw 47;}  void my_ternminate() {     cout << "I am a ternminate" << endl;     exit(0); }  void my_unexpected() {     cout << "unexpected exception thrown" << endl; //   throw Up();     throw 8;     //如果在unexpected中继续抛出异常,抛出的是规格说明中的 则会被捕捉程序继续执行     //如果抛出的异常不在异常规格说明中分两种情况     //1.异常规格说明中有bad_exception ,那么会导致抛出一个bad_exception     //2.异常规格说明中没有bad_exception 那么会导致程序调用ternminate函数    // exit(0); }  int main() { set_terminate(my_ternminate); set_unexpected(my_unexpected); for(int i = 1;i <=3;i++) {      //当抛出的异常,并不是异常规格说明中的异常时      //会导致最终调用系统的unexpected函数,通过set_unexpected可以      //用来设置自己的unexpected汗函数     try {         f(i);        }catch(Up) {         cout << "Up caught" << endl;        }catch(Fit) {         cout << "Fit caught" << endl;        }catch(bad_exception) {         cout << "bad exception" << endl;        } } }

上面的代码说明了异常规格说明的基本语法,以及unexpected函数的作用,以及如何自定义自己的unexpected函数,还讨论了在 unexpected函数中继续抛出异常的情况下,该如何处理抛出的异常.C++11中取消了这种异常规格说明.引入了一个noexcept函数,用于表 明这个函数是否会抛出异常

void recoup(int) noexecpt(true);  //recoup不会抛出异常
void recoup(int) noexecpt(false); //recoup可能会抛出异常

此外还提供了noexecpt用来检测一个函数是否不抛出异常.

异常安全

异常安全我觉得是一个挺复杂的点,不光光需要实现函数的功能,还要保存函数不会在抛出异常的情况下,出现不一致的状态.这里举一个例子,大家在实现 堆栈的时候经常看到书中的例子都是定义了一个top函数用来获得栈顶元素,还有一个返回值是void的pop函数仅仅只是把栈顶元素弹出,那么为什么没有 一个pop函数可以 即弹出栈顶元素,并且还可以获得栈顶元素呢?

template<typename T> T stack<T>::pop() {     if(count == 0)         throw logic_error("stack underflow");     else         return data[--count]; }

如果函数在***一行抛出了一个异常,那么这导致了函数没有将退栈的元素返回,但是Count已经减1了,所以函数希望得到的栈顶元素丢失了.本质原 因是因为这个函数试图一次做两件事,1.返回值,2.改变堆栈的状态.***将这两个独立的动作放到两个独立的函数中,遵守内聚设计的原则,每一个函数只做 一件事.我们 再来讨论另外一个异常安全的问题,就是很常见的赋值操作符的写法,如何保证赋值操作是异常安全的.

class Bitmap {...}; class Widget {     ... private:     Bitmap *pb;  }; Widget& Widget::operator=(const Widget& rhs) {     delete pb;     pb = new Bitmap(*rhs.pb);     return *this; }

上面的代码不具备自我赋值安全性,倘若rhs就是对象本身,那么将会导致*rhs.pb指向一个被删除了的对象.那么就绪改进下.加入证同性测试

Widget& Widget::operator=(const Widget& rhs) {     If(this == rhs) return *this; //证同性测试     delete pb;     pb = new Bitmap(*rhs.pb);     return *this; }

但是现在上面的代码依旧不符合异常安全性,因为如果delete pb执行完成后在执行new Bitmap的时候出现了异常,则会导致最终指向一块被删除的内存.现在只要稍微改变一下,就可以让上面的代码具备异常安全性.

Widget& Widget::operator=(const Widget& rhs) {     If(this == rhs) return *this; //证同性测试     Bitmap *pOrig = pb;     pb = new Bitmap(*rhs.pb); //现在这里即使发生了异常,也不会影响this指向的对象     delete pOrig;     return *this;   }

这个例子看起来还是比较简单的,但是用处还是很大的,对于赋值操作符来说,很多情况都是需要重载的.

“C++中的异常处理机制介绍”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: C++中的异常处理机制介绍

本文链接: https://www.lsjlt.com/news/287235.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++中的异常处理机制介绍
    本篇内容介绍了“C++中的异常处理机制介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!异常处理增强错误恢复能力是提高代码健壮性的最有力的途...
    99+
    2023-06-17
  • Java 中的异常处理机制详情介绍
    目录介绍 Java 中的异常介绍 Error介绍 ExceptionJava 异常类的结构如何处理函数抛出的异常吞掉 or 抛出受检异常 or 非受检异常处理异常的原则尽量不要捕获通...
    99+
    2022-11-13
  • C++ 异常处理 catch(...)介绍
    如果要想使一个catch block能抓获多种数据类型的异常对象的话,怎么办C++标准中定义了一种特殊的catch用法,那就是” catch(…)”。 感性认识 1、catch(…)...
    99+
    2022-11-15
    异常 catch
  • Java中的异常处理机制介绍(非常全面!)
    目录异常处理机制编译时异常处理机制异常处理方式一异常处理方式二异常处理方式三运行时异常处理机制总结异常处理机制 异常的默认处理流程如下: 默认会在出现异常的代码那里自动的创建一个异常...
    99+
    2023-01-18
    Java异常处理机制原理 java中包含异常处理机制 java异常处理方式两种
  • C/C++中异常处理详解及其作用介绍
    目录概述异常处理异常处理机制函数声明指定异常练习案例一案例二概述 作为一名专业写 Bug, 编程一天改 bug 一周的程序媛. 学会异常处理是非常重要的. 我们不仅要考虑没有错误的理...
    99+
    2022-11-12
  • Java异常机制的详细介绍
    本篇内容主要讲解“Java异常机制的详细介绍”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java异常机制的详细介绍”吧!1.异常的概述1.1什么是异常?异常:程序在运行过程中发生由于外部问题导...
    99+
    2023-06-02
  • C++11中异常处理机制详解
    目录一、异常的引入二、C++异常的关键字三、异常的抛出与处理规则四、异常缺陷的处理五、自定义异常体系六、异常规范七、异常安全八、异常的优缺点1.优点2.缺点一、异常的引入 传统的C语...
    99+
    2022-11-13
  • php异常与错误处理机制概念及使用介绍
    目录基本概念标准异常处理自定义异常处理异常处理最佳使用场景控制异常代码影响范围保证数据统一性错误处理错误等级定义标准错误处理自主处理非致命错误自主处理致命错误抛出自定义错误基本概念 ...
    99+
    2022-11-13
  • java中异常类型及异常处理的详细介绍
    一、异常实现及分类1.先看下异常类的结构图上图可以简单展示一下异常类实现结构图,当然上图不是所有的异常,用户自己也可以自定义异常实现。上图已经足够帮我们解释和理解异常实现了:java相关免费视频教程:java教学视频所有的异常都是从Thro...
    99+
    2015-02-22
    java入门 java 异常类型 异常处理
  • 如何实现C++中的异常处理机制?
    如何实现C++中的异常处理机制引言:异常处理是编程中非常重要的一部分,它可以提高程序的可靠性和稳定性。在C++中,异常处理机制可以帮助我们处理程序中的错误和异常情况,使得程序的控制流能够在异常发生时进行变更,从而避免程序的崩溃。本文将介绍C...
    99+
    2023-11-02
    异常处理机制 (Exception Handling) C++异常 (C++ Exceptions) 异常处理语句 (E
  • 实例介绍java中异常的处理方式
    首先处理异常主要有两种方式:一种try catch,一种是throws。一、try catchtry{} 中放入可能发生异常的代码。catch{}中放入对捕获到异常之后的处理。其中catch中e.printStackTrace()作用就是,...
    99+
    2016-10-03
    java 异常 处理方式
  • js中的异常处理try...catch使用介绍
    在JavaScript可以使用try...catch来进行异常处理。例如: 复制代码 代码如下: try { foo.bar();} catch (e) { alert(e.name...
    99+
    2022-11-15
    异常处理 try catch
  • c++异常处理机制是怎么样的
    这篇文章主要介绍了c++异常处理机制是怎么样的,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。当一个程序出现错误时,它可能的情况有3种:语法错误,运行时错误和逻辑错误。语法错误...
    99+
    2023-06-17
  • 详解C++中的异常和错误处理机制
    目录什么是异常处理C++中的异常处理机制什么是错误处理C++中的错误处理机制结论什么是异常处理 异常处理是指在程序执行过程中发生异常或错误时,程序能够捕获并处理这些异常或错误的机制。...
    99+
    2023-05-19
    C++异常处理机制 C++异常处理 C++错误处理机制 C++ 错误处理
  • Android的Touch事件处理机制介绍
    Android的Touch事件处理机制比较复杂,特别是在考虑了多点触摸以及事件拦截之后。 Android的Touch事件处理分3个层面:Activity层,ViewGroup层...
    99+
    2022-06-06
    touch Android
  • C++异常处理机制及常见问题分析
    C++异常处理机制及常见问题分析引言:C++是一种强大的编程语言,它提供了异常处理机制来处理程序运行过程中的错误和异常情况。异常处理是一种控制流程的机制,用于在特定的条件下,将控制从当前执行点转移到另一个处理点。本文将介绍C++中的异常处理...
    99+
    2023-10-22
    C++异常处理 问题分析
  • Java异常的处理机制
    图片解析: 1.生成字节码文件的过程可能产生编译时异常(checked),由字节码文件到在内存中加载、运行类此过程可能产生运行时异常(unchecked), 2.JAVA程序在执行...
    99+
    2022-11-12
  • Java的异常处理机制
    本篇内容介绍了“Java的异常处理机制”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Java 异常处理异常是程序中的一些错误,但并不是所有的...
    99+
    2023-06-02
  • 【JAVA 异常处理机制】
    文章目录 前言1.java异常处理机制2.try-catch3.finally块4.自动关闭特性5.throw关键字6.throws关键字7.throws的重写规则8.异常分类9.异常API10.自定义异常总结: 前言 在Ja...
    99+
    2023-08-23
    java 开发语言 学习 intellij idea
  • Java异常类型介绍及处理方法
    前言: Java异常,大家都很熟悉。但是对于具体怎么分类的,JVM对其怎么处理的,代码中怎么处理的,应该怎么使用,底层怎么实现的等等,可能就会有些不是那么清晰。本文基于此详细捋一下异...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作