【问题标题】:What happens when a string literal is passed to a function accepting a const std::string & in C++?将字符串文字传递给接受 const std::string & 在 C++ 中的函数时会发生什么?
【发布时间】:2020-12-17 14:56:34
【问题描述】:

正如您可能从标题中猜到的那样,我想了解将 std::string 作为 const 引用传递给函数时究竟会发生什么,因为今天早些时候我遇到了一些我不太了解的情况完全。这是一些代码:

#include <string>
#include <stdio.h>

struct Interface {
    virtual void String1(const std::string &s) = 0;
    virtual void String2(const std::string &s) = 0;
    virtual void DoSomething() = 0;
};

struct SomeClass : public Interface {
    void String1(const std::string &s) override { s1 = s.c_str(); }
    void String2(const std::string &s) override { s2 = s.c_str(); }
    void DoSomething() override { printf("%s - %s\n", s1, s2); }

private:
    const char *s1, *s2;
};

struct AnotherClass {
    AnotherClass(Interface *interface) : interface(interface) {
        this->interface->String1("Mean string literal");
    }

    void DoTheThing() {
        std::string s("Friendlich string literal");
        interface->String2(s);
        interface->DoSomething();
    }

private:
    Interface *interface = nullptr;
};

int main(int argc, char **argv) {
    SomeClass some_class;
    AnotherClass another_class(&some_class);

    another_class.DoTheThing();
}

当在 SomeClass 中对 s1 和 s2 使用 const char * 时,程序将打印 Friendlich 字符串文字 - Friendlich 字符串文字[some rubbish] - Friendlich 字符串文字 而不是 平均字符串文字 - Friendlich 字符串文字 正如我所期望的那样。

当为 s1 和 s2 切换到 std::string 时,它按预期工作,打印 Mean string literal - Friendlich string literal

我和同事的猜测是,AnotherClass 的 ctor 中的字符串超出了范围,但 SomeClass 由于 c_str() 仍然存储了字符串的地址。

当对 s1 和 s2 使用 std::string 而不是 const char * 时,它实际上会创建一个副本,因此超出范围不是问题。像这样:

struct SomeClass : public Interface {
    void String1(const std::string &s) override { s1 = s; }
    void String2(const std::string &s) override { s2 = s; }
    void DoSomething() override { printf("%s - %s\n", s1.c_str(), s2.c_str()); }

private:
    std::string s1, s2;
};

那么……到底发生了什么?为什么它不能与 const char * 一起使用?为什么它可以与 std::string 一起使用?

【问题讨论】:

    标签: c++ stdstring


    【解决方案1】:

    当您将字符串文字传递给接受const std::string&amp; 的函数时,会发生以下事件:

    • 字符串文字转换为const char*
    • 创建了一个临时的std::string 对象。它的内部缓冲区被分配,并通过从const char* 复制数据进行初始化,直到看到终止的空值。参数引用这个临时对象。
    • 函数体运行。
    • 假设函数正常返回,临时对象会在函数返回和调用表达式结束之间的某个未指定时间点被销毁。

    如果从参数中保存了c_str()指针,由于指向临时对象的内部缓冲区,所以在临时对象被销毁后,它就变成了一个悬空指针。

    如果函数接受std::string,也会出现类似的问题。 std::string 对象将在函数被调用时创建,并在函数返回或不久之后销毁,因此任何保存的 c_str() 指针都将变为悬空状态。

    如果函数接受const std::string&amp; 并且参数的类型为std::string,那么在调用函数时不会创建新对象。引用指的是现有对象。 c_str() 指针将保持有效,直到原始 std::string 对象被销毁。

    【讨论】:

    • 从字符串文字创建的 const char * 会发生什么?
    • @Marco 指针本身也是一个临时对象,在全表达式结束时被销毁。这无关紧要,除非您打算保留指针或对指针本身的引用。这就像我们通常不担心表达式 2 + 3 中的 3 的生命周期一样。
    • 所以...如果我将参数更改为 const char * 它将保留它直到 SomeClass 不再需要它?
    • @Marco 如果将参数更改为const char*,则该指针值将指向字符串文字。字符串文字将一直存在,直到程序终止。因此,复制该指针并在以后取消引用它是安全的。但是,保存指向指针参数本身的指针并不安全,因为指针参数会在函数返回后被销毁。
    【解决方案2】:

    char * 不是对象,它是指向存在于其他上下文中的字符的指针。如果将这样的指针分配给临时变量,或临时变量中包含的数据,则在销毁临时变量时它将无效。在那之后使用它会产生未定义的行为。

    当您有std::string 的成员变量时,在赋值时会创建一个副本,因此临时变量是否被销毁并不重要。

    【讨论】:

    • 为什么要复制呢?因为成员变量的类型是std::string?但论据不是参考吗?
    • @Marco a std::string 始终包含自己的字符数据,因此如果它接受引用,它仍然会制作自己的副本。
    • 非常感谢:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-08
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 2020-11-26
    • 2011-10-18
    • 1970-01-01
    相关资源
    最近更新 更多