问题背景:
函数
func()返回的string对象在调用c_str()方法赋值给一个指针时,报错。而且在长度大于22时候才报错。
报错信息为
AddressSanitizer: stack-use-after-scope
后来发现,返回的string的函数定义为 string func();改为 const string& func(); 后问题解决。
最开始以为是返回栈上临时对象的原因导致的。为栈上数据并未覆盖或者清空,导致报错。 但string类实现中,对象是保存在堆上,假设不对。
查阅了相关的资料以后,发现是c++中对于右值的生命周期并没有明确规定,对象的具体析构时间不确定,取决于编译器实现。
继承string类,
class MyString : public string
{
public:
MyString(const char * aa) :string(aa)
{
cout << "MyString " ; //<<endl;
printf("%p\n", this);
}
MyString(const MyString& bb) :string(bb)
{
cout << "MyString copy ";// <<endl;
printf("%p\n", this);
}
MyString(const MyString&& bb) :string(bb)
{
cout << "MyString move ";// <<endl;
printf("%p\n", this);
}
~MyString()
{
cout << "~MyString ";
printf("%p\n", this);
}
};
在以下函数中测试
class A
{
public:
A(MyString&& name) :name_(name)
{}
MyString GetName()
{
cout << "GetName" <<endl;
MyString a = name_;
return a;
}
private:
MyString name_;
};
void func(vector<shared_ptr<A>>& mmm, vector<const char*>& res)
{
cout << "----------------------------" <<endl;
for(auto item: mmm)
{
const char* tmp = item->GetName().c_str(); // 3
//cout << item->GetName() << endl;
printf("c_str %s\n", tmp);
res.push_back(tmp);
}
}
void func1(vector<shared_ptr<A>>& mmm)
{
cout << "----------------------------" <<endl;
for(int i = 0; i < 1; i++)
{
mmm.emplace_back(make_shared<A>("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); // 1
cout << mmm.back()->GetName().c_str() <<endl; // 2
printf("%p\n", mmm.back().get());
}
}
int main()
{
vector<shared_ptr<A>> mmm;
func1(mmm);
vector<const char*> res;
func(mmm, res);
cout << "----------------------------" <<endl;
for(auto item : res)
{
printf("c_str %s\n", item);
}
}
函数输出
MyString 0x7fff07cb92d0 // 1 中的临时对象
MyString copy 0x13ad040 // 1 中make_shared 调用的拷贝构造
~MyString 0x7fff07cb92d0 // 1 中的临时对象 析构
GetName // 2 调用GetName
MyString copy 0x7fff07cb92d0 // 2 调用GetName产生临时对象的拷贝构造
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa // 2 print
~MyString 0x7fff07cb92d0 // 2 调用GetName产生的拷贝构造对象析构
0x13ad040
----------------------------
GetName
MyString copy 0x7fff07cb92d0 // 3 调用GetName产生临时对象的拷贝构造
~MyString 0x7fff07cb92d0 // 3 调用GetName产生的拷贝构造对象析构
c_str // 2 print
----------------------------
c_str
~MyString 0x13ad040
根据输出可以看到,在调用c_str之前,MyString就已经析构了。导致了编译失败。
结论:c++中,对于右值(将亡值)的生命周期,并不保证。具体析构释放时间不确定
解决方法:
- 返回一个引用
- 使用左值接受,然后进行下一步操作