有这么个情景,程序需要打开设备,而打开设备会失败。为了模拟这个情景,c++代码中写了个设备类,由于在进行其他操作之前需要先打开这个设备,所以这里要对类做点设计。经过查阅和分析,想了两种方法只调用一次就能够获得已经打开的设备对象。

  一是二段构造,二是构造函数中抛异常。

1. 二段构造

  原来的构造函数中只是对内部变量进行赋值,construct函数负责完成打开设备操作,另外还要有个init静态函数把构造函数和construct函数包起来。在操作中发现,二段构造中的new操作似乎是不可避免的,这就需要在主函数中加入delete操作。

#include <iostream>
#include <Windows.h>

//模拟用来打开设备的函数,可能会失败,代表一个可能会失败的操作
int open(HANDLE* handle)
{
	srand(time(NULL));
	int num = rand() % 100;
	if (num < 50)
		return 0;
	else
		return -1;
}

class Device
{

public:

	Device() {
		handle = NULL;
	}
	~Device() {


	};
	bool construct() {//二段构造,这里只打开设备
		int ret = open(&handle);
		if (ret != 0)
		{
			std::cout << "open device fail" << " ret: " << ret << std::endl;
			return false;
		}
		std::cout << "open device success" << std::endl;
		return true;
	};

	static Device* init()//二段构造
	{
		Device* device = new Device();//这里的new看上去无法避免
		if (device->construct() == false)
		{
			delete device;
			return nullptr;
		}
		return device;
	}

private:
	HANDLE handle;//打开设备中往往有个句柄
};

int main(int argc, char* argv[])
{
	//Device* dev = Device::init();
	//delete dev;//之前的办法要多加一个关闭的操作
	
	//用一下这个方法就可以不用多加一步了
	auto ptr = std::unique_ptr<Device>{ Device::init() };

	//这里会有各种操作
	//....
}

2. 构造加异常

  在构造函数中加入try catch的处理异常的方法。由于构造函数没有返回值,拿到的对象不一定是成功打开了设备,还需要对内部变量handle进行判断。

#include <iostream>
#include <Windows.h>

//模拟用来打开设备的函数,可能会失败,代表一个可能会失败的操作
int open(HANDLE* handle)
{
	srand(time(NULL));
	int num = rand() % 100;
	if (num < 50)
		return 0;
	else
		return -1;
}

class Device
{

public:

	Device() {
		handle = NULL;
		try
		{//这里加异常判断
			int ret = open(&handle);
			if(ret !=0)
				throw std::string("can't open device\n");
		}
		catch (const std::string& str)
		{
			std::cout << str << std::endl;
		}
	}
	~Device() {


	};
	private:
	HANDLE handle;//打开设备中往往有个句柄
};

int main(int argc, char* argv[])
{
	Device dev;

	//这里是各种操作

	system("pause");
}

笔者还是更认可构造函数中加异常的操作,如果用二段操作,需要记得delete掉new出来的对象,这样就破坏了RAII。

两种方法都是可以的,前一种方法经过unique_ptr改进后,也是符合RALL的。