C++中的异常处理机制详解

异常处理

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

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

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

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

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

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

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

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

#include
#include
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
#include
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
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
#include
#include
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
#include
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
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
#include
#include
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
#include
#include
#include
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;
}

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

(0)

相关推荐

  • C++中异常处理的基本思想及throw语句抛出异常的使用

    异常处理基本思想 C++的异常处理的基本思想大致可以概括为传统错误处理机制.通过函数返回值来处理错误. 1)C++的异常处理机制使得异常的引发和异常的处理不必在同一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理.上层调用者可以再适当的位置设计对不同类型异常的处理. 2)异常是专门针对抽象编程中的一系列错误处理的,C++中不能借助函数机制,因为栈结构的本质是先进后出,依次访问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试,如图 3)异常超

  • 举例说明自定义C++异常处理的实例

    举例说明自定义C++异常处理的实例 例1:自定义一个继承自excepton的异常类myException C++标准中,定义在<stdexcept>中的任何异常类都派生自exception Class,本例也只是简单地由exception继承,在try段抛出一个异常并捕捉.代码如下: /*++ test.cpp version:1.0 decript:define a exception class named myException derived from base class excep

  • C++的try块与异常处理及调试技术实例解析

    本文以示例形式简述了C++ try块的异常处理与调试技术,有助于读者复习并加深对try块的了解. 一.格式: 抛出异常throw 异常类型例如throw runtime_error("Data must refer to same ISBN"); try{ program-statements }catch(exception-specifier) { handler-statement; }catch(exception-specifier) { handler-statement;

  • C++ 异常处理 catch(...)介绍

    如果要想使一个catch block能抓获多种数据类型的异常对象的话,怎么办?C++标准中定义了一种特殊的catch用法,那就是" catch(-)". 感性认识 1.catch(-)到底是一个什么样的东东,先来个感性认识吧!看例子先: 复制代码 代码如下: int main() { try { cout << "在 try block 中, 准备抛出一个异常." << endl; //这里抛出一个异常(其中异常对象的数据类型是int,值为1

  • C++编程异常处理中try和throw以及catch语句的用法

    若要在 C++ 中实现异常处理,你可以使用 try.throw 和 catch 表达式. 首先,使用 try 块将可能引发异常的一个或多个语句封闭起来. throw 表达式发出信号,异常条件(通常是错误)已在 try 块中发生.你可以使用任何类型的对象作为 throw 表达式的操作数.该对象一般用于传达有关错误的信息.大多数情况下,建议你使用 std::exception 类或标准库中定义的派生类之一.如果其中的类不合适,建议你从 std::exception 派生自己的异常类. 若要处理可能引

  • c++异常处理机制示例及详细讲解

    这两天我写了一个测试c++异常处理机制的例子,感觉有很好的示范作用,在此贴出来,给c++异常处理的初学者入门.本文后附有c++异常的知识普及,有兴趣者也可以看看. 下面的代码直接贴到你的console工程中,可以运行调试看看效果,并分析c++的异常机制. 复制代码 代码如下: #include "stdafx.h" #include<stdlib.h> #include<crtdbg.h> #include <iostream> // 内存泄露检测机

  • C++之异常处理详解

    程序中的错误分为编译时的错误和运行时的错误.编译时的错误主要是语法错误,比如:句尾没有加分号,括号不匹配,关键字错误等,这类错误比较容易修改,因为编译系统会指出错误在第几行,什么错误.而运行时的错误则不容易修改,因为其中的错误是不可预料的,或者可以预料但无法避免的,比如内存空间不够,或者在调用函数时,出现数组越界等错误.如果对于这些错误没有采取有效的防范措施,那么往往会得不到正确的运行结果,程序不正常终止或严重的会出现死机现象.我们把程序运行时的错误统称为异常,对异常处理称为异常处理.C++中所

  • C++中的异常处理机制详解

    异常处理 增强错误恢复能力是提高代码健壮性的最有力的途径之一,C语言中采用的错误处理方法被认为是紧耦合的,函数的使用者必须在非常靠近函数调用的地方编写错误处理代码,这样会使得其变得笨拙和难以使用.C++中引入了异常处理机制,这是C++的主要特征之一,是考虑问题和处理错误的一种更好的方式.使用错误处理可以带来一些优点,如下: 错误处理代码的编写不再冗长乏味,并且不再和正常的代码混合在一起,程序员只需要编写希望产生的代码,然后在后面某个单独的区段里编写处理错误的嗲吗.多次调用同一个函数,则只需要某个

  • C++11中异常处理机制详解

    目录 一.异常的引入 二.C++异常的关键字 三.异常的抛出与处理规则 四.异常缺陷的处理 五.自定义异常体系 六.异常规范 七.异常安全 八.异常的优缺点 1.优点 2.缺点 一.异常的引入 传统的C语言处理异常的方式有两种: 1.终止程序:使用assert断言语句,如果发生内存错误等,比如内存泄漏或者除0错误,都会直接终止程序. 2.返回错误码:通过错误码判断发生的异常的类型是什么,如系统的很多库的接口程序通过把错误码放到errno中,表示错误. 在实际中的C语言程序基本都是通过返回错误码的

  • java-流的使用完结与异常处理机制(详解)

    1.1 java.io.objectInputStream 对象输入流:用于将一组字节(通过对象输出流写出对象而转换的一组字节)读取并转换为对应的对象.对象输出流将对象写出时转换为一组字节的过程,称为:对象序列化对象输入流将这组字节读取并还原会对象的过程,称为:对象反序列化 1.2 java.io.Serializable Serializable序列化接口 当一个类实现了Serializable接口后,应当在当前类中添加一个常量: 序列化版本号serialVersionUID 序列化版本号若不

  • Java try-catch-finally异常处理机制详解

    Java中的try-catch-finally异常处理 一.异常处理 异常(Exception):是在运行发生的不正常情况. 原始异常处理: if(条件) { 处理办法1 处理办法2 处理办法3 } if(条件) { 处理办法4 处理办法5 处理办法6 } 代码阅读性差,臃肿不堪,与正常流程代码结合的很紧密,所以,在JAVA中进行一系列的改良,将一系列常见的问题,用面向对象的思考方式,对其进行了描述.封装. class 处理办法 { 处理办法1() { 举例1 } 处理办法2() { 举例2 }

  • Java SpringMVC拦截器与异常处理机制详解分析

    目录 拦截器(interceptor)的作用 拦截器快速入门 案例:用户登录权限控制 拦截器方法说明 SpringMVC异常处理 异常处理的思路 异常处理两种方式 拦截器(interceptor)的作用 Spring MVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. 将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain).在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用.拦截器也是AOP思

  • SpringMVC项目异常处理机制详解

    目录 1.异常分类 2.自定义项目业务异常 3.自定义项目系统异常 4.其他异常 5.异常代码 6.异常处理器 7.异常发生 1.异常分类 通常分为三类:系统异常(SystemException),业务异常(BusinessException)和其他异常(Exception) 业务异常指由于用户的不规范操作产生的异常,如不合法的参数传入 系统异常指项目运行过程中可预计但无法避免的异常,如数据库宕机 其他异常指开发者未曾预料到的异常 2.自定义项目业务异常 public class Busines

  • Java中的反射机制详解

    Java中的反射机制详解 反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧! 一,先看一下反射的概念: 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接.但是反射使用不当会成本很高! 看概念很晕的,继续往下

  • Android中的binder机制详解

    前言 Binder做为Android中核心机制,对于理解Android系统是必不可少的,关于binder的文章也有很多,但是每次看总感觉看的不是很懂,到底什么才是binder机制?为什么要使用binder机制?binder机制又是怎样运行的呢?这些问题只是了解binder机制是不够的,需要从Android的整体系统出发来分析,在我找了很多资料后,真正的弄懂了binder机制,相信看完这篇文章大家也可以弄懂binder机制. 1.Binder是什么? 要理解binder,先要知道IPC,Inter

  • Java开发过程中关于异常处理的详解

    1.运行java时,出现了异常: 我这里是因为:arr[3]不存在: java.lang.ArrayIndexOutOfBoundsException: 3 public class btyf { public static void main(String[] args){ int[] arr={1,2,3}; System.out.println(arr[0]); System.out.println(arr[3]); System.out.println(arr[1]); //1 异常 A

  • C++中函数匹配机制详解

    首先,编译器会确定候选函数然后确定可行函数可行函数,再从可行函数中进一步挑选 候选函数:重载函数集中的函数 可行函数:可以调用的函数 最后进行寻找最佳匹配 有以下几种规则 1.该函数的每个实参的匹配都不劣于其他可行函数 2.至少有一个实参的匹配优于其他可行函数的匹配 3.满足上面两种要求的函数有且只有一个 如果上面三个要求都没满足,则出现二义性 一些演示 各有一个精确匹配的实参,编译器报错,不满足条件3 error void func(int a,int b) { cout << "

随机推荐