在 C++ 中将字符串文字传递给接受 const std::string & 的函数时会发生什么?
What happens when a string literal is passed to a function accepting a const std::string & in C++?
正如您可能从标题中猜到的那样,我想了解将 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 string literal - Friendlich string literal 或 [some rubbish] - Friendlich string literal 而不是 平均字符串文字 - 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?
当您将字符串文字传递给接受 const std::string&
的函数时,会发生以下事件:
- 字符串文字转换为
const char*
- 创建了一个临时
std::string
对象。它的内部缓冲区被分配,并通过从 const char*
复制数据来初始化,直到看到终止 null。参数指的是这个临时对象。
- 函数体运行。
- 假设函数 returns 正常,临时对象在函数 returns 和调用表达式结束之间的某个未指定点被销毁。
如果c_str()
指针是从参数中保存的,由于指向临时对象的内部缓冲区,因此在销毁临时对象后成为悬垂指针。
如果函数接受std::string
,也会出现类似的问题。 std::string
对象将在调用函数时创建,并在函数 returns 或之后不久销毁,因此任何保存的 c_str()
指针都将变为悬空。
如果函数接受 const std::string&
并且参数的类型为 std::string
,但是,调用函数时不会创建新对象。引用引用现有对象。 c_str()
指针将保持有效,直到原始 std::string
对象被销毁。
A char *
不是对象,它是指向其他上下文中存在的字符的指针。如果将这样的指针分配给临时变量,或临时变量中包含的数据,则在临时变量被销毁时它将无效。在那之后使用它会产生未定义的行为。
当你有std::string
的成员变量时,在赋值时会生成一个副本,所以临时对象是否被销毁都没有关系。
正如您可能从标题中猜到的那样,我想了解将 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 string literal - Friendlich string literal 或 [some rubbish] - Friendlich string literal 而不是 平均字符串文字 - 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?
当您将字符串文字传递给接受 const std::string&
的函数时,会发生以下事件:
- 字符串文字转换为
const char*
- 创建了一个临时
std::string
对象。它的内部缓冲区被分配,并通过从const char*
复制数据来初始化,直到看到终止 null。参数指的是这个临时对象。 - 函数体运行。
- 假设函数 returns 正常,临时对象在函数 returns 和调用表达式结束之间的某个未指定点被销毁。
如果c_str()
指针是从参数中保存的,由于指向临时对象的内部缓冲区,因此在销毁临时对象后成为悬垂指针。
如果函数接受std::string
,也会出现类似的问题。 std::string
对象将在调用函数时创建,并在函数 returns 或之后不久销毁,因此任何保存的 c_str()
指针都将变为悬空。
如果函数接受 const std::string&
并且参数的类型为 std::string
,但是,调用函数时不会创建新对象。引用引用现有对象。 c_str()
指针将保持有效,直到原始 std::string
对象被销毁。
A char *
不是对象,它是指向其他上下文中存在的字符的指针。如果将这样的指针分配给临时变量,或临时变量中包含的数据,则在临时变量被销毁时它将无效。在那之后使用它会产生未定义的行为。
当你有std::string
的成员变量时,在赋值时会生成一个副本,所以临时对象是否被销毁都没有关系。