C++实现defer声明方法详解

目录
  • 一、前言
  • 二、实现
    • 2.1 相关技术
    • 2.2 实现
    • 2.3 对外接口
  • 三、测试

本文代码地址:https://github.com/pengguoqing/samples_code

一、前言

   在Go 语言里面有一个 defer 声明, 它的作用是将函数调用保存在列表中, 函数返回时依次调用列表中的函数。

之前实现简易版的智能指针文章中指出, 智能指针内部就是利用的 RAII特点, 将对象的声明周期使用栈来管理。 因此可以借鉴 Go语言中的 defer逻辑, 然后结合RAII的特点来实现一个 C++ 版本的 defer, 依次来实现在当前作用域内必须要调用的函数。

  关于离开作用域时某些函数必须调用的一个经典例子就是文件的关闭, 如下:

	FILE *file = fopen("readme.ext", "rb");
	if (file == NULL)
	{
		return;
	}
	if (caseOne)
	 {
		// dosomething
		fclose(file); // 必须手动关闭文件
		return;
	}
	//caseTwo
	//dosomething
	fclose(file);     // 必须手动关闭文件
	return;

   这种情况下需要显示的在多个 return 的之前调用 fclose(file)关闭文件, 一方面维护的时候可能会忘记关闭文件, 而且代码看起来也不够简洁。熟悉 RAII 技术后, 常用的方式是实现一个相关 scope 类封装, 在文件开发成功后将 FILE 句柄传入构造函数,在离开作用域时利用栈资源销毁调用 scope 类的析构函数 关闭文件。这当前时一种解决方案, 但是每当遇到一个这种场景时就需要手动封装一个相关的 scope 类。所以需要一个 类似 defer的东西方便使用。

二、实现

2.1 相关技术

①编译器类型自动推导;

②Lambda表达式(仿函数);

③RAII;

④转发引用

2.2 实现

  对函数调用只需要一次, 所以需要类似 unique_ptr的方式, 管理仿函数对象的类只能被移动, 不能被拷贝和赋值,声明如下:

public:
	inline CXDeferImpl(const Functor& functor);
	inline CXDeferImpl(Functor&& functor);
	inline CXDeferImpl(CXDeferImpl&& another);
	inline ~CXDeferImpl();
private:
	//不允许拷贝, 赋值
	CXDeferImpl(const CXDeferImpl& another)			  = delete;
	CXDeferImpl operator=(const CXDeferImpl& another) = delete;
	CXDeferImpl operator=(CXDeferImpl&& another)	  = delete;

类成员只需要一个仿函数对象和一个是否有效的标志:

private:
    Functor  m_func;
    bool     m_valid;

2.3 对外接口

  以宏的方式对外提供调用, 利用 Lambda表达式封装需要调用的函数,毕竟它的内部就是一个实现仿函数的类, 再让编译器自动推导 Lambda 类型来构造一个 CXDeferImpl实例, 每次 defer() 声明都创建一个对象 CXDeferImpl。

#define CONCAT_(a, b, c) a##b##c
#define CONCAT(a, b, c)  CONCAT_(a, b, c)
#define defer(x) \
  auto CONCAT(defer_, __LINE__, __COUNTER__) = MakeDeferIns([&]{x;})

三、测试

  当然是是用万能并且经典的 “Hello world!” 了, 哈哈哈。测试其离开作用域时能不能完整的输出的 “Hello world!”, 代码如下:

void TestDefer()
{
  defer(cout<<"world!"<<endl);
  cout<<"Hello ";
}
void TestDefer2()
{
  {
      defer(TestDefer());
      cout<<"Test defer in scope"<<endl;
  }
  cout << "Test defer are ok?" << endl;
  defer(cout<<"TestDefer2"<<endl);
}
void TestDefer3(int a)
{
  {
      defer(cout << "test param func " << a << endl);
  }
  defer(cout << "TestDefer3" << endl);
}
int main()
{
  TestDefer2();
  TestDefer3(100);

  return 0;
}

输出如下:

到此这篇关于C++实现defer声明方法详解的文章就介绍到这了,更多相关C++ defer内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++实现Go的defer功能(示例代码)

    在Go语言中有一个关键字:defer,它的作用就是延迟执行后面的函数,在资源释放方面特别有用,比如下面一段C/C++的示例代码: void test() { FILE* fp = fopen("test.txt", "r"); if (nullptr == fp) return; if (...) { fclose(fp); return; } if (...) { fclose(fp); return; } if (...) { fclose(fp); retur

  • C++实现defer声明方法详解

    目录 一.前言 二.实现 2.1 相关技术 2.2 实现 2.3 对外接口 三.测试 本文代码地址:https://github.com/pengguoqing/samples_code 一.前言    在Go 语言里面有一个 defer 声明, 它的作用是将函数调用保存在列表中, 函数返回时依次调用列表中的函数. 之前实现简易版的智能指针文章中指出, 智能指针内部就是利用的 RAII特点, 将对象的声明周期使用栈来管理. 因此可以借鉴 Go语言中的 defer逻辑, 然后结合RAII的特点来实

  • PHP 页面编码声明方法详解(header或meta)

    php的header来定义一个php页面为utf编码或GBK编码 php页面为utf编码 header("Content-type: text/html; charset=utf-8"); php页面为gbk编码 header("Content-type: text/html; charset=gb2312"); php页面为big5编码 header("Content-type: text/html; charset=big5"); 通常情况以

  • java 中enum的使用方法详解

    java 中enum的使用方法详解 enum 的全称为 enumeration, 是 JDK 1.5 中引入的新特性,存放在 java.lang 包中. 下面是我在使用 enum 过程中的一些经验和总结. 原始的接口定义常量 public interface IConstants { String MON = "Mon"; String TUE = "Tue"; String WED = "Wed"; String THU = "Thu

  • java中重写equals()方法的同时要重写hashcode()方法(详解)

    object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true: 注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码.如下: (1) 当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true (2) 当obj

  • php及codeigniter使用session-cookie的方法(详解)

    1.读写cookie <1>原生 setcookie('name','value',time) 设置失败,没有正常写入浏览器,测试失败,原因未知 <2>CI框架 $this->input->set_cookie("views","test10",1000); echo $_COOKIE["views"];//此方法获取值时,如果值不存在会报错,当然可以先用isset($_COOKIE["views&q

  • Java枚举的使用方法详解

     Java枚举的使用方法详解 前言  你代码中的flag和status,都应该用枚举来替代 很多人都说,枚举在实际开发中很少用到,甚至就没用到.因为,他们的代码往往是这样子的: public class Constant { /* * 以下几个变量表示英雄的状态 */ public final static int STATUS_WALKING = 0;//走 public final static int STATUS_RUNNINGING = 1;//跑 public final stati

  • JavaScript中windows.open()、windows.close()方法详解

    windows.open()方法详解: window.open(URL,name,features,replace)用于载入指定的URL到新的或已存在的窗口中,并返回代表新窗口的Window对象.它有4个可选的 参数: URL:一个可选的字符串,声明了要在新窗口中显示的文档的 URL.如果省略了这个参数,或者它的值是空字符串,那么新窗口就不会显示任何文档. name:一个可选的字符串,该字符串是一个由逗号分隔的特征列表,其中包括数字.字母和下划线,该字符声明了新窗口的名称.这个名称可以用作标记

  • JavaScript数据类型的存储方法详解

    一个很基础的知识点,JavaScript中基本数据类型和引用数据类型是如何存储的. 由于自己是野生程序员,在刚开始学习程序设计的时候没有在意内存这些基础知识,导致后来在提到"什么什么是存在栈中的,栈中只是存了一个引用"这样的话时总是一脸懵逼.. 后来渐渐的了解了一些内存的知识,这部分还是非常有必要了解的. 基本数据结构 栈 栈,只允许在一段进行插入或者删除操作的线性表,是一种先进后出的数据结构. 堆 堆是基于散列算法的数据结构. 队列 队列是一种先进先出(FIFO)的数据结构. Jav

  • C++ 中RTTI的使用方法详解

    C++ 中RTTI的使用方法详解 RTTI是运行阶段类型识别(Runtime Type Identification)的简称.这是新添加到c++中的特性之一,很多老式实现不支持.另一些实现可能包含开关RTTI的编译器设置.RTTI旨在为程序在运行阶段确定对象类型提供一种标准方式.很多类库已经成为其父类对象提供了实现这种方式的功能.但由于c++内部并不支持,因此各个厂商的机制通常互不兼容.创建一种RTTI语言标准将使得未来的库能够彼此兼容. c++有3个支持RTTI的元素 如果可能的话,dynam

  • Android Parcelable接口使用方法详解

     Android Parcelable接口使用方法详解 1. Parcelable接口 Interface for classes whose instances can be written to and restored from a Parcel. Classes implementing the Parcelable interface must also have a static field called CREATOR, which is an object implementin

随机推荐