作为参数传递的std::string的生命周期

3
我希望我理解得正确。在下面的c++函数中,将创建三个std::string实例:
  • 一个是在caller()中为s1所创建的,我相信这被称为赋值构造函数
  • callee()函数的参数s2通过其复制构造函数创建了第二个实例
  • 第三个实例s3通过其复制构造函数创建
我的理解是否正确?如果是,那么这三个实例在超出作用域时都会被清除吗?我并不是真正询问此代码是否好或不好,只是想知道我的理解是否正确。
void caller(void) {
    std::string s1 = "hi";
    callee(s1);
}

void callee(std::string s2) {
    std::string s3 = s2;
}

1
据我所知,"赋值构造函数"这种东西是不存在的。您有一个"转换构造函数",它可以将一种类型转换为另一种类型。 - Mooing Duck
2个回答

3

不要假设副本被制作在它们看起来被制作的所有地方。在实践中,复制遗漏比你想象的要经常发生。编译器可以自由地优化掉多余的副本,即使副本具有副作用:

void caller(void) {
    std::string s1 = "hi";
    //theoretically, s1 is copied here
    //in practice, the compiler will optimize the call away
    functionCallee(s1);
}

void callee(std::string s2) {
    //although s2 is passed by value, it's probably no copy was created
    //most likely, it's exactly the same as s1 from the calling context
    std::string s3 = s2;
}

此外,如果方法是内联的,并且编译器检测到没有副作用发生,那么字符串可能甚至不会被创建。

谢谢!按照我所示的方式做,不会有内存泄漏吗? - John Fitzpatrick
4
如果从不使用newmalloc,就不会出现内存泄漏问题。 - Luchian Grigore
@NicolBolas 我不是说一定会发生,但它可能性很大。 - Luchian Grigore
@LuchianGrigore:“如果你从不使用new或malloc,就永远不会出现内存泄漏。”这是无稽之谈。即使你使用make_shared将每个指针转换为shared_ptr,仍然可以通过循环引用泄漏内存。不要认为你因为没有直接调用new而免于内存泄漏的困扰。 - Nicol Bolas
@NicolBolas 我从来没有直接说过。:| 如果你调用了一些内部调用new的方法,那么当然会发生泄漏... - Luchian Grigore
显示剩余9条评论

2

你的答案几乎正确。

无论是创建三个还是四个字符串(取决于是否省略了s1的构造),在每种情况下都会调用构造函数来构造它们。尽管看起来如此,但没有调用任何赋值运算符。

void caller(void) {
    //A temporary std::string is constructed with the
    //basic_string(const CharT* s, const Allocator& alloc = Allocator())
    //constructor.
    //(In the call, `s` is initialized to point to the first element of "hi".)
    //This temporary is then move constructed in to s1.
    //The move constructor is
    //basic_string(basic_string&& other)
    //This move construction may be elided.
    std::string s1 = "hi"; //At the end of the full expression (ie, at the semicolon)
                           //the lifetime of the temporary string ends (unless
                           //the construction of s1 is elided, in which
                           //case the temporary becomes s1, and its lifetime ends
                           //with s1).
    //s2 is copy constructed from s1
    //The copy constructor is
    //basic_string(const basic_string& other)
    callee(s1);
    //the lifetime of s1 ends
}

void callee(std::string s2) {
    //s3 is copy constructed from s2
    std::string s3 = s2;
    //the lifetime of s3 ends
    //the lifetime of s2 ends
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接