C++ goto用法

作者:梨梨喵
链接:https://www.zhihu.com/question/27922046/answer/73347171
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

关于goto的使用, 一个是跳出多层循环时使用, 还有一个是在性能考虑. 其他场景下基本上没有goto什么事, 特别在现代C++的情况下.我的个人结论是除非是非goto不可的情况下绝不使用goto. 喜欢用goto说明姿势水平还不够高, 没有找到能很好描述逻辑的方式和工具, 还需要学习一个.之前在读源码的时候也发现了一些使用goto情况, 如@蓝色说的统一的错误处理情况和统一的资源管理的情况. 不过在有了C++11 lambda后goto存在的意义就不大了, 统一的错误处理和资源管理可以交给RAII+lambda.如有以下一情况:

HANDLE func()
{
	HANDLE h = CreateSomeObject(...);
	//...
	if(error0_occur)
	{
		goto err_exit;
	}

	//...

	if(error1_occur)
	{
		goto err_exit;
	}

	//...

	return h;
err_exit:
	CloseHandle(h);
	return nullptr;
}

在有了C++11 lambda后, 可以交给RAII来管理了, goto在这种情景下使用的意义并不大了

class ScopeGuard
{
public:
	explicit ScopeGuard(std::function<void()> onExit)
		: __onExit(onExit), __dismissed(false)
	{ }

	~ScopeGuard()
	{
		if (!__dismissed)
		{
			__onExit();
		}
	}

	void Dismiss(bool dismissed=true)
	{
		__dismissed = dismissed;
	}

	ScopeGuard(ScopeGuard const&) = delete;
	ScopeGuard& operator=(ScopeGuard const&) = delete;
	// non-copyable

private:
	std::function<void()> __onExit;
	bool __dismissed;
};

HANDLE func()
{
	HANDLE h = CreateSomeObject(...);
	ScopeGuard onErrorExit([&] { CloseHandle(h); h=nullptr; });
	//...
	if(error0_occur)
	{
		return nullptr;
	}

	//...

	if(error1_occur)
	{
		return nullptr;
	}

	//...
	onErrorExit.Dismiss()  // no error occur
	return h;
}

另一点, 在关于性能方面goto在VM上有一定作用如一个简单的指令循环如下:

while (byte_code.size()>IP)
{
	switch (byte_code[IP])
	{

	case BYTE_CODE::INSTRUCTION_0:
	{
		//some action for instruction 0
		IP += sizeof(instruction_0);
		break;
	}
	case BYTE_CODE::INSTRUCTION_1:
	{
		//some action for instruction 1
		IP += sizeof(instruction_1);
		break;
	}
	case BYTE_CODE::INSTRUCTION_2:
	{
		//some action for instruction 2
		IP += sizeof(instruction_2);
		break;
	}

	//...
	//more cases

	}
}

这么写的话在switch和while上出现了至少两个分支语句, 一次指令循环中要进行一次条件跳转和三次无条件跳转(开启switch跳转表优化后)在使用goto语句后可以大大减少在这些控制语句上的性能消耗, 配合GCC的拓展Labels as Values使用.

static const void *next_instr[] = { 
	&&byte_code_halt,
	&&byte_code_instr0,
	&&byte_code_instr1,
	&&byte_code_instr2,
	//...
}

switch (byte_code[IP])  //for initial instruction
{

case BYTE_CODE::HALT:  //end of loop
{
byte_code_halt:
	break;
}
case BYTE_CODE::INSTRUCTION_0:
{
byte_code_instr0:
	//some action for instruction 0
	IP += sizeof(instruction_0);
	goto *(next_instr[byte_code[IP]]);
}
case BYTE_CODE::INSTRUCTION_1:
{
byte_code_instr1:
	//some action for instruction 1
	IP += sizeof(instruction_1);
	goto *(next_instr[byte_code[IP]]);
}
case BYTE_CODE::INSTRUCTION_2:
{
byte_code_instr2:
	//some action for instruction 2
	IP += sizeof(instruction_2);
	goto *(next_instr[byte_code[IP]]);
}

//...
//more cases

}

在指令流结束的位置插入一条halt指令表示终止. 这样就一次指令循环中只有一次无条件跳转. 性能上能获得一定的提高.之前在我自己写VM的时候做profile, 发现在switch和while上至少消耗了12%的性能, 改用goto后获得了7%的性能的提升.在这一点上goto还是有一定的意义的.

「C++11」Lambda 表达式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值