下面的问题是从一个巨大的项目中提炼出来的,也是我能够想到的问题的最小例子。寻找堆栈损坏错误的解释
我知道,从std::string
得到的结果很糟糕,它已经在我们的代码库中发生了变化,但我试图理解这里隐藏的内容。
的代码在释放模式崩溃上的Visual C++ 2017
Microsoft Visual Studio Community 2017
Version 15.2 (26430.14) Release
Visual C++ 2017 00369-60000-00001-AA257
只(以速度优化)。没有速度优化,它不会在发布模式下崩溃。
#include <string>
#include <string_view>
#include <vector>
struct my_string : public std::string
{
__declspec(noinline)
my_string::my_string(const std::string_view& str) :
std::string(str.data(), str.size())
{}
template <typename T>
my_string& arg(T)
{
return *this;
}
};
struct my_string_view : public std::string_view
{
my_string_view(const std::string_view::value_type* val) :
std::string_view(val) {}
template <typename... PARAMS>
my_string arg(PARAMS&&... prms) {
return my_string(*this).arg(std::forward<PARAMS>(prms)...);
}
};
template <typename T>
struct basic_color
{
T r, g, b, a;
basic_color() : r(0), g(0), b(0), a(255) {}
template <typename U>
explicit basic_color(const basic_color<U>& c) :
r(c.r), g(c.g), b(c.b), a(c.a)
{}
};
using color = basic_color<std::uint8_t>;
using float_color = basic_color<float>;
__declspec(noinline)
void change_float_color(float_color& color)
{
color.r = 0.1f;
}
int main()
{
std::vector<float_color> colors = { {} };
float sum = 0;
for (std::uint32_t i = 0; i < 1; ++i)
{
float_color fc;
change_float_color(fc);
color c(fc);
std::vector<std::string> msgs;
msgs.push_back(my_string_view("").arg(c.r));
msgs.push_back(my_string_view("").arg(c.g));
sum += fc.b - colors[i].b;
}
return static_cast<int>(sqrt(sum));
}
在Visual Studio中的错误是这样的(看看的msgs
和colors
破碎的尺寸在底部):
我的猜测是,std::vector<std::string>::push_back(std::string&&)
与通话my_string
有问题(切片行为)。但是如何破坏堆栈(或堆栈指针)呢?
有没有人有一个想法可能发生在这里或我如何能找出?
Here是我的项目,以防万一任何人有兴趣重现问题。
使用调试器,数据断点是这里选择的武器。 –
“因为std :: string没有虚拟析构函数,所以给push_back赋予的r值被切片了。”切片与虚拟析构函数无关。无论如何,这些值都会被切片。 “因此,派生类(my_string)的析构函数永远不会被调用,一些垃圾仍然在堆栈中”这没有任何意义。 'sizeof(my_string)== sizeof(std :: string)'这不会使一点区别。 “看看破损的大小”编译器可以随意对这些对象做任何事情,因为它们将永远不会再被使用。 –
>我的第一个猜测是,由于std :: string没有虚拟析构函数,因此赋予push_back的r值被切片。因此,无论析构函数是否被调用,派生类(my_string)的析构函数都不会被调用<为这个类分配的内存(在这种情况下为堆栈)。而且,'〜my_string' **被**调用,因为当'my_string'被复制到'std :: string'中时发生切片,并且原始字符串不是,也不能被切片。 – Hedede