右值存活时间

295 阅读2分钟

问题背景:

函数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++中,对于右值(将亡值)的生命周期,并不保证。具体析构释放时间不确定

解决方法:

  1. 返回一个引用
  2. 使用左值接受,然后进行下一步操作