文章

我踩过的c++的坑 续续

之前我写过一篇长期更新的《我踩过的c++的坑》,因为时间很长,文章太长了,查看起来太麻烦了,所以以后遇到的坑都记在这篇《我踩过的c++的坑 续》里。

距离上一次写这段话又过了两年。工作之后我依然在c++之苦海里挣扎。遇到的一些坑,都写到内部博客里了。东一块西一块地不方便检索,所以以后c++非业务的坑记录于此。

我踩过的c++的坑 续

我踩过的c++的坑

吐槽一下我的新博客框架竟然会将++识别为md的下划线语法。。。。这里我注意了,但是之前的博客里的c++不是全被坑了。。。可能是主题原因

需要规避指针拥有过长的生命周期,或指向可能被底层逻辑修改的内容

业务中有偶现的执行失败。通过日志分析可以发现是某个指针所指向的内容被污染。首先想到可不可能是多线程导致的内存池问题或者普通的堆溢出篡改了此处。阅读代码后排除,因为是一处单线程逻辑,且指针不存在共享。精读代码后发现此指针在生命周期内没有任何不合理的调用,业务逻辑也是正确的。内网又难以复现此bug。只能写了一段逻辑,如果遇到bug就回滚业务并发送警告。

后续在内网中测试别的模块时偶现了这个bug。通过修改数据库、内存重现了这个bug。通过内存断点的方式发现是被函数的某个调用函数的调用函数的调用函数调用的vectorpushback篡改了内容。顿时有了头绪,一查果然,这个指针指向的资源是通过一个极为复杂的函数分配的,最后返回的是一个vector的某元素的地址。业务函数又在某个地方又申请了一份这个资源,此时可能触发vector pushback导致的扩容,导致第一个指针变成野指针。万幸复现的几率极低,且由于业务逻辑不会写野指针而造成更严重的堆栈破坏。。。如果这里破坏了堆栈导致别的地方崩溃的话,影响就极大了。

教训

  • 不能忽视任何内存相关的bug
  • 如果指针不能在生命周期内对资源有完全所有权,就不能使用指针。特别是要注意不能对任何非直接分配的内存或者STL容器内的元素取值。
  • 指针的作用区域要短小而精悍。这里的业务逻辑加起来可能有几千行,竟然存在一个指针从头到脚都被信任,这是不可以的。任何指针一旦涉及到非当时开发的,可以被绝对信任,且不涉及内存的逻辑外的函数,都需要在函数过后重新进行标准的资源get流程。需要时刻警惕指针是否被篡改、或者资源被移动。如果涉及到对指针指向的结构体的读写,需要显式校验指针有效性就更不用说了。
  • 尽量使用标号和句柄指向资源而非指针

强大的工具更需要谨慎的使用。

error C2084: 函数“XXX”已有主体

这个情况往往出现在函数重载时,有两个重载实例仅仅在一个类型上有区别,而且这两个类型仅仅是由typedef相互转化的。

不要和go的类型混淆了。c++的typedef本质上不会创造一种新的类型,在预编译的时候就会被处理成真正的类型。

这样编译时就会出现两个签名完全一样的函数,自然是编不过的。

注意空迭代器

要将空迭代器和指针一样警惕对待。需要注意的是迭代器本身就是指针。而end()返回的是容器的last,这个东西看源码可以发现就是个普通的迭代器。

typedef typename _Alloc::pointer _Tptr;
typedef _Tptr pointer;
pointer _Mylast;	// pointer to current end of sequence
    _NODISCARD pointer operator->() const noexcept {
#if _ITERATOR_DEBUG_LEVEL != 0
        const auto _Mycont = static_cast<const _Myvec*>(this->_Getcont());
        _STL_VERIFY(_Ptr, "can't dereference value-initialized vector iterator");
        _STL_VERIFY(
            _Mycont->_Myfirst <= _Ptr && _Ptr < _Mycont->_Mylast, "can't dereference out of range vector iterator");
#endif // _ITERATOR_DEBUG_LEVEL != 0

        return _Ptr;
    }

而这个迭代器,为了满足它-1可以得到尾元素的特性,它本质上是一个指向尾元素后的指针。也就是个野指针。当我们试图使用它时,他会访问非法的地址。而考虑这些数据存放在堆里,如果访问的话,会发生非常惨烈的堆溢出。如果运气好的话是简单的读脏数据或者当成crash,运气不好的话就是在某一个时间段后看在后面的堆空间回收或者分配后随机崩溃。

要时刻警惕,任何可能产生end迭代器的地方都要和指针一样进行,检验。

License:  CC BY 4.0