按值 return a std::string 安全吗?
Is it safe to return a std::string by value?
在下面的代码中,一个字符串封装在一个class,Foo.
调用 Foo::getText() return 字符串值。这将创建字符串对象的第二个实例,但两个字符串对象现在都指向堆上的同一个字符缓冲区。
删除Foo实例时,封装的字符串会自动销毁,从而删除堆上的char缓冲区。
即使getText()
return按值是一个字符串,生成的字符串对象仍然依赖于原始字符串对象的生命周期,以保留它们相互指向的字符缓冲区。
这是否意味着向终端打印 retval
是对已在堆上释放的内存的无效访问?
class Foo
{
Foo(const char* text) :
str(text)
{
}
std::string getText()
{
return str;
}
std::string str;
};
int main()
{
Foo pFoo = new Foo("text");
std::string retval = foo.getText();
delete pFoo;
cout << retval; // invalid memory access to char buffer?
}
我认为很多人认为,因为字符串是按值 return 编辑的,所以他们不需要关心原始字符串在 Foo 中的生命周期。这个问题与字符串并不严格相关,但实际上适用于任何 class 具有在销毁时释放的封装指针。但是,当涉及到字符串时,这里的最佳实践是什么?
- 从不 return 字符串值?
- 如果保证原始字符串的生命周期,则按值仅 return 个字符串?
- 总是复制字符串?
return std::string(retval.c_str());
- 与
getText()
的调用者签订合同?
编辑:
我想我被 RVO 误导了。此示例中的所有三个字符串 return a c_str 位于同一地址。 RVO 是罪魁祸首吗?
class Obj
{
public:
Obj() : s("text")
{
std::printf("%p\n", s.c_str());
}
std::string getText() { return s; }
std::string s;
};
int main()
{
Obj* pObj = new Obj();
std::string s1(pObj->getText());
std::string s2 = pObj->getText();
delete pObj;
std::printf("%p\n", s1.c_str());
std::printf("%p\n", s2.c_str());
}
结果:
0x600022888
0x600022888
0x600022888
This creates a second instance of the string object, but both string objects now point to the same char buffer on the heap.
不,他们没有。
std::string
s 拥有自己的内容。当你复制一个 std::string
时,你复制了它的缓冲区。
I think a lot of people assume that, because the string was returned by value, they need not be concerned about the lifetime of the original string within Foo.
那些人是对的。没有"sharing".
你的 return 值很好,你不必多想。
我想对@LightnessRacesInOrbit 的回答补充几点:
Never return string by value?
从不通过引用或指针 return 本地 string
。实际上,从不通过引用或指针 return anything 本地。按颜值来说就好了。
Only return strings by value if the lifetime of the original string is
guaranteed?
同样,您在倒退思考。只有 return 通过引用或指针 如果保证原件的生命周期。
Always make a copy of the string? return std::string(retval.c_str());
如果 C++ 无法将字符串移出 (RVO/NRVO),它会自动为您执行此操作。无需手动复制。
Enforce a contract with the caller of getText()?
不需要,因为你得到了一份副本
I think a lot of people assume that, because the string was returned by value, they need not be concerned about the lifetime of the original string within Foo. This problem isn't strictly related to strings, but really applies to any class with encapsulated pointers that are free'd upon destruction.
他们的假设是正确的。任何具有(工作的)复制构造函数的 class 都可以根据需要进行复制,并且每个复制的实例都完全独立于其他实例。复制构造函数负责复制堆-space.
在下面的代码中,一个字符串封装在一个class,Foo.
调用 Foo::getText() return 字符串值。这将创建字符串对象的第二个实例,但两个字符串对象现在都指向堆上的同一个字符缓冲区。
删除Foo实例时,封装的字符串会自动销毁,从而删除堆上的char缓冲区。
即使getText()
return按值是一个字符串,生成的字符串对象仍然依赖于原始字符串对象的生命周期,以保留它们相互指向的字符缓冲区。
这是否意味着向终端打印 retval
是对已在堆上释放的内存的无效访问?
class Foo
{
Foo(const char* text) :
str(text)
{
}
std::string getText()
{
return str;
}
std::string str;
};
int main()
{
Foo pFoo = new Foo("text");
std::string retval = foo.getText();
delete pFoo;
cout << retval; // invalid memory access to char buffer?
}
我认为很多人认为,因为字符串是按值 return 编辑的,所以他们不需要关心原始字符串在 Foo 中的生命周期。这个问题与字符串并不严格相关,但实际上适用于任何 class 具有在销毁时释放的封装指针。但是,当涉及到字符串时,这里的最佳实践是什么?
- 从不 return 字符串值?
- 如果保证原始字符串的生命周期,则按值仅 return 个字符串?
- 总是复制字符串?
return std::string(retval.c_str());
- 与
getText()
的调用者签订合同?
编辑:
我想我被 RVO 误导了。此示例中的所有三个字符串 return a c_str 位于同一地址。 RVO 是罪魁祸首吗?
class Obj
{
public:
Obj() : s("text")
{
std::printf("%p\n", s.c_str());
}
std::string getText() { return s; }
std::string s;
};
int main()
{
Obj* pObj = new Obj();
std::string s1(pObj->getText());
std::string s2 = pObj->getText();
delete pObj;
std::printf("%p\n", s1.c_str());
std::printf("%p\n", s2.c_str());
}
结果:
0x600022888
0x600022888
0x600022888
This creates a second instance of the string object, but both string objects now point to the same char buffer on the heap.
不,他们没有。
std::string
s 拥有自己的内容。当你复制一个 std::string
时,你复制了它的缓冲区。
I think a lot of people assume that, because the string was returned by value, they need not be concerned about the lifetime of the original string within Foo.
那些人是对的。没有"sharing".
你的 return 值很好,你不必多想。
我想对@LightnessRacesInOrbit 的回答补充几点:
Never return string by value?
从不通过引用或指针 return 本地 string
。实际上,从不通过引用或指针 return anything 本地。按颜值来说就好了。
Only return strings by value if the lifetime of the original string is guaranteed?
同样,您在倒退思考。只有 return 通过引用或指针 如果保证原件的生命周期。
Always make a copy of the string? return std::string(retval.c_str());
如果 C++ 无法将字符串移出 (RVO/NRVO),它会自动为您执行此操作。无需手动复制。
Enforce a contract with the caller of getText()?
不需要,因为你得到了一份副本
I think a lot of people assume that, because the string was returned by value, they need not be concerned about the lifetime of the original string within Foo. This problem isn't strictly related to strings, but really applies to any class with encapsulated pointers that are free'd upon destruction.
他们的假设是正确的。任何具有(工作的)复制构造函数的 class 都可以根据需要进行复制,并且每个复制的实例都完全独立于其他实例。复制构造函数负责复制堆-space.