c++ 从vector扩容看noexcept应用场景
2021-03-28 18:25
标签:提高 调试 r++ define 资料 ESS 释放 some modern c++11提供了关键字 所以我们需要了解以下两点: 我们先从std::vector入手来看一下第一点。 我们知道,vector有自己的capacity,当我们调用 但是如果在扩容元素时出现异常怎么办? 这种扩容方式比较完美,有异常时也会保持上游调用 但是为什么说比较完美,因为这里扩容还是copy的,当vector内是一个类且持有资源较多时,这会很耗时。所以c++11推出了一个新特性: 利用 刚才总结了利用 上面提到了 很多人的第一念头可能是:我的函数现在看起来明显不会抛异常,又说声明 这个问题想要讨论清楚,我们首先需要知道以下几点: 所以在我们的代码内部调用复杂,链路较长,且随时有可能加入新feature时,过早给函数加上 目前主流的观点是: 最后我们看一下vector如何实现利用 这里就不贴大段的代码了,每个平台的实现可能都不一样,我们只关注vector是怎么判断调用 其中利用到的核心技术有: 核心代码: 这里用 然后上游利用生成的 其中 总结一下过程就是: 大家可以用下面这段简单的代码在自己的平台打断点调试一下: 这篇文章C++ NOEXCEPT AND MOVE CONSTRUCTORS EFFECT ON PERFORMANCE IN STL CONTAINERS介绍了noexcept move constructor对耗时以及内存的影响,这里不重复赘述了,感兴趣的可以自己试一下。 参考资料: c++ 从vector扩容看noexcept应用场景 标签:提高 调试 r++ define 资料 ESS 释放 some modern 原文地址:https://www.cnblogs.com/zhangyachen/p/13625683.htmlnoexcept
,用来指明某个函数无法——或不打算——抛出异常:void foo() noexcept; // a function specified as will never throw
void foo2() noexcept(true); // same as foo
void bar(); // a function might throw exception
void bar2() noexcept(false); // same as bar
noexcept
有什么优点,例如性能、可读性等等。noexcept
。noexcept优点
push_back
但是vector容量满时,vector会申请一片更大的空间给新容器,将容器内原有的元素copy到新容器内:
push_back
时原有的状态。move
,它会将资源从旧元素中“偷”给新元素(对move不熟悉的同学可以自己查下资料,这里不展开说了)。应用到vector扩容的场景中:当vector中的元素的移动拷贝构造函数是noexcept
时,vector就不会使用copy方式,而是使用move方式将旧容器的元素放到新容器中:move
的交换类资源所有权的特性,使用vector扩容效率大大提高,但是当发生异常时怎么办:
原有容器的状态已经被破坏,有部分元素的资源已经被偷走。若要恢复会极大增加代码的复杂性和不可预测性。所以只有当vector中元素的move constructor
是noexcept
时,vector扩容才会采取move方式来提高性能。noexcept
如何提高vector扩容。实际上,noexcept
还大量应用在swap
函数和move assignment
中,原理都是一样的。noexcept使用场景
noexcept
可以使用的场景:
noexcept
编译器可以生成更高效的代码,那能加就加呗。但是事实是这样吗?
noexcept
一致性检查,例如下述代码是合法的:void g(){
... //some code
}
void f() noexcept
{
… //some code
g();
}
noexcept
的函数抛出异常时,程序会被终止并调用std::terminate();noexcept
可能不是一个好的选择,因为noexcept
一旦加上,后续再去掉也会变得困难 : 调用方有可能看到你的函数声明为noexcept,调用方也会声明为noexcept
。但是当你把函数的noexcept
去掉却没有修改调用方的代码时,当异常抛出到调用方会导致程序终止。
throw()
noexcept
。# if __cplusplus >= 201103L
# define _GLIBCXX_NOEXCEPT noexcept
# else
# define _GLIBCXX_NOEXCEPT
reference
operator*() const _GLIBCXX_NOEXCEPT
{ return *_M_current; }
pointer
operator->() const _GLIBCXX_NOEXCEPT
{ return _M_current; }
__normal_iterator&
operator++() _GLIBCXX_NOEXCEPT
{
++_M_current;
return *this;
}
__normal_iterator
operator++(int) _GLIBCXX_NOEXCEPT
{ return __normal_iterator(_M_current++); }
除了上面的要加的情况,其余的函数不要加noexcept
就可以。noexcept move constructor
扩容以及move constructor
是否声明noexcept
对扩容的性能影响。如何实现利用
noexcept move constructor
扩容copy constructor
还是move constructor
的。
template
type trait
和iterator trait
联合判断:假如元素有noexcept move constructor
,那么is_nothrow_move_constructible=1
=> __move_if_noexcept_cond=0
=> __make_move_if_noexcept_iterator
返回一个move iterator
。这里move iterator
迭代器适配器也是一个c++11新特性,用来将任何对底层元素的处理转换为一个move操作,例如:std::list<:string> s;
std::vector
move iterator
进行循环元素move:{
for (; __first != __last; ++__first, (void)++__cur) std::_Construct(std::__addressof(*__cur), *__first);
return __cur;
}
template
_Construct
就是实际copy(或者move)元素的函数。这里很关键的一点是:对move iterator进行解引用操作,返回的是一个右值引用。,这也就保证了,当__first
类型是move iterator
时,用_T1(std::forward<_args>(__args)...
进行“完美转发”才调用_T1
类型的move constructor
,生成的新对象被放到新vector的__p
地址中。
type trait
和iterator trait
生成指向旧容器的normal iterator
或者move iterator
move iterator
,那么解引用会返回右值引用,会调用元素的move constructor
,否则调用copy constructor
。class A {
public:
A() { std::cout
noexcept move constructor
对性能的影响
上一篇:JavaScript(三)
文章标题:c++ 从vector扩容看noexcept应用场景
文章链接:http://soscw.com/index.php/essay/69158.html