深入理解C++中的异常处理机制

2023-11-11

异常处理

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

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

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

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

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

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

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

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

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

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

  1. #include <iostream> 
  2. #include <csetjmp> 
  3.  
  4. using namespace std; 
  5.  
  6. class base { 
  7.     public: 
  8.         base() { 
  9.             cout << "base construct func call" << endl; 
  10.         } 
  11.         ~base() { 
  12.             cout << "~base destruct func call" << endl; 
  13.         } 
  14. }; 
  15.  
  16. jmp_buf static_buf; 
  17.  
  18. void test_base() { 
  19.     base b; 
  20.     //do something 
  21.     longjmp(static_buf,47);//进行了跳转,跳转后会发现b无法析构了 
  22.  
  23. int main() { 
  24.     if(setjmp(static_buf) == 0) { 
  25.         cout << "deal with some thing" << endl; 
  26.         test_base(); 
  27.     } else { 
  28.         cout << "catch a error" << endl; 
  29.     } 

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

异常的抛出

  1. class MyError { 
  2.     const char* const data; 
  3. public: 
  4.     MyError(const char* const msg = 0):data(msg) 
  5.     { 
  6.         //idle 
  7.     } 
  8. }; 
  9.  
  10. void do_error() { 
  11.     throw MyError("something bad happend"); 
  12.  
  13. int main() 
  14.     do_error(); 

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

异常的捕获

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

  1. try{ 
  2.     //do something 
  3.     throw string("this is exception"); 
  4. catch(const string& e) { 
  5.     cout << "catch a exception " << e << endl; 

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

异常的匹配

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

  1. #include <iostream> 
  2.  
  3. using namespace std; 
  4. int main() 
  5.     try{ 
  6.  
  7.         throw 'a'; 
  8.     }catch(int a) { 
  9.         cout << "int" << endl; 
  10.     }catch(char c) { 
  11.         cout << "char" << endl; 
  12.     } 

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

  1. //基类 
  2. class Base{ 
  3.     public: 
  4.         Base(string msg):m_msg(msg) 
  5.         { 
  6.         } 
  7.         virtual void what(){ 
  8.             cout << m_msg << endl; 
  9.         } 
  10.     void test() 
  11.     { 
  12.         cout << "I am a CBase" << endl; 
  13.     } 
  14.     protected: 
  15.         string m_msg; 
  16. }; 
  17. //派生类,重新实现了虚函数 
  18. class CBase : public Base 
  19.     public: 
  20.         CBase(string msg):Base(msg) 
  21.         { 
  22.  
  23.         } 
  24.         void what() 
  25.         { 
  26.            cout << "CBase:" << m_msg << endl; 
  27.         } 
  28. }; 
  29.  
  30. int main() 
  31.     try { 
  32.         //do some thing 
  33.     //抛出派生类对象 
  34.         throw CBase("I am a CBase exception"); 
  35.  
  36.     }catch(Base& e) {  //使用基类可以接收 
  37.         e.what(); 
  38.     } 

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

  1. try { 
  2.     //do some thing 
  3.     throw CBase("I am a CBase exception"); 
  4.  
  5. }catch(Base e) { 
  6.     e.test(); 

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

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

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

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

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

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

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

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

  1. try{ 
  2.     throw Exception("I am a exception");    
  3. }catch(...) { 
  4.     //log the exception 
  5.     throw; 

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

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

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

  1. #include <exception> 
  2. #include <iostream> 
  3. #include <cstdlib> 
  4. using namespace std; 
  5.  
  6. class MyError { 
  7.     const char* const data; 
  8. public: 
  9.     MyError(const char* const msg = 0):data(msg) 
  10.     { 
  11.         //idle 
  12.     } 
  13. }; 
  14.  
  15. void do_error() { 
  16.     throw MyError("something bad happend"); 
  17. //自定义的terminate函数,函数原型需要一致 
  18. void terminator() 
  19.     cout << "I'll be back" << endl; 
  20.     exit(0); 
  21.  
  22. int main() 
  23.     //设置自定义的terminate,返回的是原有的terminate函数指针 
  24.     void (*old_terminate)() = set_terminate(terminator); 
  25.     do_error(); 
  26. 上面的代码会输出I'll be back 

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

异常中的资源清理

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

  1. #include <iostream> 
  2. #include <string> 
  3. using namespace std; 
  4.  
  5. class base 
  6.     public: 
  7.         base() 
  8.         { 
  9.             cout << "I start to construct" << endl; 
  10.             if (count == 3) //构造第四个的时候抛出异常 
  11.                 throw string("I am a error"); 
  12.             count++; 
  13.         } 
  14.  
  15.         ~base() 
  16.         { 
  17.             cout << "I will destruct " << endl; 
  18.         } 
  19.     private: 
  20.         static int count; 
  21. }; 
  22.  
  23. int base::count = 0; 
  24.  
  25. int main() 
  26.         try{ 
  27.  
  28.             base test[5]; 
  29.  
  30.         } catch(...){ 
  31.  
  32.             cout << "catch some error" << endl; 
  33.  
  34.         } 
  35. 上面的代码输出结果是: 
  36. I start to construct 
  37. I start to construct 
  38. I start to construct 
  39. I start to construct 
  40. I will destruct 
  41. I will destruct 
  42. I will destruct 
  43. catch some error 

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

  1. #include <iostream> 
  2.  
  3. using namespace std; 
  4.  
  5. int main() try { 
  6.     throw "main"; 
  7. catch(const char* msg) { 
  8.     cout << msg << endl; 
  9.     return 1; 
  10. main函数语句块,可以捕获main函数中抛出的异常. 
  11. class Base 
  12.     public: 
  13.         Base(int data,string str)try:m_int(data),m_string(str)//对初始化列表中可能会出现的异常也会进行捕捉 
  14.        { 
  15.             // some initialize opt 
  16.        }catch(const char* msg) { 
  17.  
  18.             cout << "catch a exception" << msg << endl; 
  19.        } 
  20.  
  21.     private: 
  22.         int m_int; 
  23.         string m_string; 
  24. }; 
  25.  
  26. int main() 
  27.     Base base(1,"zhangyifei"); 

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

标准异常

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

  1. #include <stdexcept> 
  2. #include <iostream> 
  3. #include <string> 
  4. using namespace std; 
  5.  
  6. class MyError:public runtime_error { 
  7. public: 
  8.     MyError(const string& msg = "") : runtime_error(msg) {} 
  9.  
  10. }; 
  11.  
  12. //runtime_error logic_error 两个都是继承自标准异常,带有string构造函数 
  13. // 
  14. int main() 
  15.     try { 
  16.         throw MyError("my message");   
  17.     }   catch(MyError& x) { 
  18.         cout << x.what() << endl;    
  19.     } 

异常规格说明

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

  1. #include <exception> 
  2. #include <iostream> 
  3. #include <cstdio> 
  4. #include <cstdlib> 
  5. using namespace std; 
  6.  
  7. class Up{}; 
  8. class Fit{}; 
  9. void g(); 
  10. //异常规格说明,f函数只能抛出Up 和Fit类型的异常 
  11. void f(int i)throw(Up,Fit) { 
  12.     switch(i) { 
  13.         case 1: throw Up(); 
  14.         case 2: throw Fit();    
  15.     } 
  16.     g(); 
  17.  
  18. void g() {throw 47;} 
  19.  
  20. void my_ternminate() { 
  21.     cout << "I am a ternminate" << endl; 
  22.     exit(0); 
  23.  
  24. void my_unexpected() { 
  25.     cout << "unexpected exception thrown" << endl; 
  26. //   throw Up(); 
  27.     throw 8; 
  28.     //如果在unexpected中继续抛出异常,抛出的是规格说明中的 则会被捕捉程序继续执行 
  29.     //如果抛出的异常不在异常规格说明中分两种情况 
  30.     //1.异常规格说明中有bad_exception ,那么会导致抛出一个bad_exception 
  31.     //2.异常规格说明中没有bad_exception 那么会导致程序调用ternminate函数 
  32.    // exit(0); 
  33.  
  34. int main() { 
  35. set_terminate(my_ternminate); 
  36. set_unexpected(my_unexpected); 
  37. for(int i = 1;i <=3;i++) 
  38.      //当抛出的异常,并不是异常规格说明中的异常时 
  39.      //会导致最终调用系统的unexpected函数,通过set_unexpected可以 
  40.      //用来设置自己的unexpected汗函数 
  41.     try { 
  42.         f(i);    
  43.     }catch(Up) { 
  44.         cout << "Up caught" << endl;    
  45.     }catch(Fit) { 
  46.         cout << "Fit caught" << endl;    
  47.     }catch(bad_exception) { 
  48.         cout << "bad exception" << endl;    
  49.     } 

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

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

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

异常安全

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

  1. template<typename T> T stack<T>::pop() 
  2.     if(count == 0) 
  3.         throw logic_error("stack underflow"); 
  4.     else 
  5.         return data[--count]; 

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

  1. class Bitmap {...}; 
  2. class Widget { 
  3.     ... 
  4. private: 
  5.     Bitmap *pb; 
  6.  
  7. }; 
  8. Widget& Widget::operator=(const Widget& rhs) 
  9.     delete pb; 
  10.     pb = new Bitmap(*rhs.pb); 
  11.     return *this; 

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

  1. Widget& Widget::operator=(const Widget& rhs) 
  2.     If(this == rhs) return *this; //证同性测试 
  3.     delete pb; 
  4.     pb = new Bitmap(*rhs.pb); 
  5.     return *this; 

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

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

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

 

本文引自:http://developer.51cto.com/art/201512/502598.htm

相关文章:c++异常处理机制示例及讲解 

转载于:https://www.cnblogs.com/carsonzhu/p/5279063.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入理解C++中的异常处理机制 的相关文章

  • C#中unsafe的使用

    1 unsafe在C 程序中的使用场合 实时应用 采用指针来提高性能 引用非 net DLL提供的如C 编写的外部函数 需要指针来传递该函数 调试 用以检测程序在运行过程中的内存使用状况 2 使用unsafe的利弊 好处是 性能和灵活性提高
  • 每天都在谈SOA和微服务,但你真的理解什么是服务吗?

    近几年来 我一直从事着和面向服务相关的底层软件研发工作 逐渐的形成了一些自己的看法 其中我觉得比较重要的看法就是服务需要一个更准确细致的定义 简单来说 服务的本质就是行为 业务活动 的抽象 为了更好的阐述新服务的概念 并方便与传统的SOA中
  • fastcgi的环境变量

    FCGI ROLE RESPONDER SCRIPT FILENAME scripts 5 cgi QUERY STRING aaa 11111111111111 bbb 2222222222222222 ccc 3333333333333
  • 侯捷系列:c++面向对象高级编程(上)

    文章目录 基于对象的程序设计 不带有指针成员变量的类 以复数类 Complex 为例 头文件的结构 访问级别 函数设计 内联函数 构造函数 常量成员函数 参数的值传递和引用传递 返回值的值传递和引用传递 友元 操作符重载 在类内声明 pub
  • SQL 查询指定行数的数据。

    今天遇到一个关于 查询指定行数的数据 的sql查询语句问题 突然发现以前没怎么接触过 刚才想起来了 赶紧看了下文档 又上网搜了下 有了下面的东西 不知道有没有什么地方不对 oracle 先看一下文档中关于any和all的例子 很不错噢 An
  • 解决“17: 错误:程序中有游离的‘\240’,\302’

    参考链接 https blog csdn net asuphy article details 54602426 执行如下命令即可 sed i s o240 o302 g dy haikang test cpp
  • JNA模拟复杂的C类型——Java映射char*、int*、float*、double*

    文章目录 引言 Java Native Type Conversions Java和C基本类型指针对应关系 Pointer的具体用法 引言 最近项目在用Java调用C写的一些三方库 没办法直接调 用Java封装一下C的接口 这就少不了要用到
  • 互联网创业盈利模式指南

    看了很多创业的case 都有点下笔千言 离题万里的 情况 就是很多case都很精彩 但是公司 的价值最终是落实到 给创业者和投资人的回报的 因此 所有的case 最终都是 落实到盈利 模式上 一位投资人士说的很明确 中国的盈利模式很简单 就
  • C++中的RTTI

    文章目录 dynamic cast运算符 指针类型的dynamic cast 引用类型的dynamic cast typeid运算符 使用RTTI type info类 参考资料 RTTI Runtime Type Information
  • ATL字符串转换宏

    有比MultiByteToWideChar和WideCharToMultiByte更简单的字符串转换宏 你相信吗 头文件 d program files microsoft visual studio 8 vc atlmfc include
  • LeetCode题目笔记——17.19消失的两个数字

    文章目录 题目描述 题目难度 困难 方法一 暴力 代码 代码优化 方法二 数学方法 代码 总结 题目描述 题目直达 题目难度 困难 方法一 暴力 虽然题目说你能在 O N 时间内只用 O 1 的空间找到它们吗 但是也没有限制我们不能用暴力
  • R----dplyr包介绍学习

    dplyr包 plyr包的替代者 专门面对数据框 将ddplyr转变为更易用的接口 gt 来自dplyr包的管道函数 其作用是将前一步的结果直接传参给下一步的函数 从而省略了中间的赋值步骤 可以大量减少内存中的对象 节省内存 可惜的是应用范
  • Open3D(C++)实现建筑物点云立面和平面分割提取

    Open3D C 实现建筑物点云立面和平面分割提取 近年来 点云技术在城市规划 机器人地图构建等领域得到广泛应用 本篇文章将介绍如何利用Open3D C 库实现建筑物点云立面和平面分割提取 准备工作 首先需要编译安装Open3D库 本文使用
  • visual studio 一直显示正在准备解决方案

    首先重启电脑 无法解决的情况下执行以下步骤 Kill Visual Studio Open Visual Studio without loading a solution Disable AnkhSvn as Source Control
  • 【C++】运算符重载

    加号运算符重载 include
  • Java反序列化漏洞-CC1利用链分析

    文章目录 一 前置知识 1 反射 2 Commons Collections是什么 3 环境准备 二 分析利用链 1 Transform
  • C++中的并发多线程网络通讯

    C 中的并发多线程网络通讯 一 引言 C 作为一种高效且功能强大的编程语言 为开发者提供了多种工具来处理多线程和网络通信 多线程编程允许多个任务同时执行 而网络通信则是现代应用程序的基石 本文将深入探讨如何使用C 实现并发多线程网络通信 并
  • C/C++编程:令人印象深刻的高级技巧案例

    C C 编程语言在软件开发领域有着悠久的历史 由于其高效 灵活和底层访问能力 至今仍然被广泛应用 本文将介绍一些在C C 编程中令人印象深刻的高级技巧 帮助读者提升编程水平 更加高效地使用这两种强大的编程语言 一 指针运算与内存管理 C C
  • 在 OS X 上的 virtualenv 中安装 scrapy 加密时发生错误 [关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我正在安装 scrapypip in virtualenv on OS X 10 11 当它安装密码学时 它说 buil
  • 在 Solaris 上,使用 gcc 编译的库与使用 cc 生成的库的使用方式是否相同?

    我目前正在尝试编译 libxml2在 Solaris 上 当我运行源代码提供的 configure 脚本时 会自动使用 gcc 和 g 编译器 但是 我想使用 cc 和 CC 编译器 所以我跑 configure CC cc CXX CC

随机推荐

  • 分类与回归树(CART)- 机器学习ML

    参考 1 统计学习方法 李航 2 https www cnblogs com en heng p 5035945 html 3 http blog csdn net baimafujinji article details 53269040
  • MindMap: Knowledge Graph Prompting Sparks Graph of Thoughts in Large Language Models

    本文是LLM系列文章 针对 MindMap Knowledge Graph Prompting Sparks Graph of Thoughts in Large Language Models 的翻译 思维导图 大型语言模型中的知识图谱提
  • 深度学习实战15(进阶版)-让机器进行阅读理解+你可以变成出题者提问

    大家好 我是微学AI 今天给大家带来一个机器阅读理解的项目 利用ERNIE的预训练模型进行微调训练 添加自己的数据集进行训练 训练好就可以利用功能进行阅读式信息抽取啦 也可以问机器一些简单的问题进行抽取 今天采用的paddle深度学习框架
  • Mybatis-plus

    1 简介 Mybatis plus是mybatis的增强工具 在mybatis的基础上只做增强不做改变 是为了简化开发和提高效率而生 mybatis plus只作用于单表的增删改查 联表操作还需要mybatis 2 Mybatis plus
  • elasticserach(一)

    文章目录 1 elasticsearch简介 1 1 正向索引和倒排索引 1 2 文档 1 3 索引和映射 1 4 elasticsearch与mysql概念对比 2 部署单点es和kibana 2 1 创建网络 2 2 拉取镜像 2 3
  • 编译原理基础知识+笔记(1)

    一 编译原理概述 1 翻译程序 把某一种语言程序 称为源语言程序 等价地转换成另一种语言程序 称为目标语言程序 的程序 2 编译程序 把某一种高级语言程序等价地转换成另一种低级语言程序 如汇编语言或机器语言程序 的程序 又分为 诊断编译程序
  • 阿里4年测试经验分享 —— 测试外包干了3年后,我废了...

    去年国庆 我分享了一次一位阿里朋友的技术生涯 大家反响爆蓬 感觉十分有意思 今天我来分享一下我另一位朋友的真实经历 是不是很想听 没错 我这位朋友是曾经外包公司的测试开发 而且一干就是三年 三年后 他说他废了 虽说废的不是很彻底 但这三年他
  • 分分钟提高效率的18个神仙网站,你都用过几个?

    1 ipaddress ip查询工具 https www ipaddress com 不知道本机IP 一进入网址就可以查到 还能查询到ip的详情信息 追踪域名 端口信息 2 json 在线解析工具 https www json cn 开发时
  • 《C++ Primer》学习笔记(十三):拷贝控制

    C Primer 学习笔记 十三 拷贝控制 拷贝 赋值与销毁 拷贝构造函数 拷贝赋值运算符 析构函数 三 五法则 使用 default 阻止拷贝 拷贝控制和资源管理 行为像值的类 定义行为像指针的类 交换操作 对象移动 右值引用 移动构造函
  • 3月10日--3月16日(共17小时,剩4543小时)

    3月10日 共3小时 上午3小时DX11 下午单位有事 3月11日 共3小时 早上5 00起床 OSGEARTH视频教程第十讲 单位有事 3月12日 共5小时 3月13日3小时 3月14日 3小时 周末0小时 共17小时
  • 【问题】multiple definition of `_start'

    使用GCC编译器的时候 程序能够编译通过 但是结果却有问题 我查找了好多地方 还是不知道问题出在什么地方 后来才发现是因为一个变量未初始化造成的 感觉以前在使用VS的时候很少会在这种问题上耽误时间 于是想到很少用到的 Wall 参数 我对一
  • 最新Dubbo-admin+Zookeeper搭建遇到的一些问题

    Zookeeper搭建 下载zookeeper压缩包并解压 下载地址 http www apache org dyn closer cgi zookeeper 进入conf目录下将 zoo sample cfg 改名为 zoo cfg 进入
  • 免费AI计算资源

    推荐几个提供免费GPU计算资源的平台 助力你的AI之路 1 Kaggle Kernel 2 百度AI 3 Google Colaboratory 原文链接 https blog csdn net mrjkzhangma article de
  • Docker 安装与Tomcat部署

    Docker 安装与Tomcat部署 虚拟机 VMware Linux环境 CentOS 7 镜像 images 容器 contenor 一 VMware安装Linux镜像 1 Linux配置固定IP Linux虚拟机配置静态IP 二 Fi
  • CodeSmith 使用教程(17) Merge策略

    前面介绍了CodeSmith使用的基本用法 通过代码模板来生成代码 但如果你修改了自动生成的代码 再次使用代码模板生成代码后 你修改的代码也就丢失了 CodeSmith 支持多种 合并 Merge 来解决这个问题 以保留你自己修该过的部分
  • GeoServer 安装教程

    准备内容 安装环境 win10 64位专业版 安装文件 geoserver 2 15 2 安装步骤 安装JDK 1 安装GeoServer是基于Java的环境 所以需要先装Jdk环境 2 前往官网下载Java SE http www ora
  • 动态住宅代理VS静态住宅代理,怎么选择?

    现在 越来越多的海外代理服务商均支持动态住宅IP与静态住宅IP 很多小伙伴就疑惑 这二者有什么区别呢 哪个更好 其实 没有哪个更好 只有哪一个更合适您的业务 无论动态住宅IP还是静态住宅IP都来自真实的住宅IP地址 都可以提供IP隐匿作用
  • MySQL下载及使用navicat连接mysql数据库(含下载地址、超具体细节、推荐数据库教程)

    目录 下载地址 安装流程 第一步 开始安装 第二步 类型选择 第三步 developer default 第四步 execute 第五步 服务器配置窗口 第六步 网络类型配置窗口 第七步 第八步 服务器密码设置窗口 第九步 服务器名称窗口
  • 60 openEuler 22.03-LTS 搭建MySQL数据库服务器-安装、运行和卸载

    文章目录 60 openEuler 22 03 LTS 搭建MySQL数据库服务器 安装 运行和卸载 60 1 安装 60 2 运行 60 3 卸载 60 openEuler 22 03 LTS 搭建MySQL数据库服务器 安装 运行和卸载
  • 深入理解C++中的异常处理机制

    异常处理 增强错误恢复能力是提高代码健壮性的最有力的途径之一 C语言中采用的错误处理方法被认为是紧耦合的 函数的使用者必须在非常靠近函数调用的地方编 写错误处理代码 这样会使得其变得笨拙和难以使用 C 中引入了异常处理机制 这是C 的主要特