C++ 如何在不复制的情况下使用std::string?
我有一个班级说C++ 如何在不复制的情况下使用std::string?,c++,string,stl,parameter-passing,pass-by-reference,C++,String,Stl,Parameter Passing,Pass By Reference,我有一个班级说 class Foo { public: void ProcessString(std::string &buffer) { // perform operations on std::string // call other functions within class // which use same std::string string } void
class Foo
{
public:
void ProcessString(std::string &buffer)
{
// perform operations on std::string
// call other functions within class
// which use same std::string string
}
void Bar(std::string &buffer)
{
// perform other operations on "std::string" buffer
}
void Baz(std::string &buffer)
{
// perform other operations on "std::string" buffer
}
};
此类尝试使用std::string
缓冲区在以下条件下使用各种方法对其执行操作:
- 我不想传递我已经拥有的
的副本std::string
- 我不想创建这个类的多个对象
// Once an object is created
Foo myObject;
// We could pass many different std::string's to same method without copying
std::string s1, s2, s3;
myObject.ProcessString(s1);
myObject.ProcessString(s2);
myObject.ProcessString(s3);
我可以使用该字符串并将其指定为类成员,以便其他使用该字符串的函数可以了解它
但我们似乎无法拥有引用类成员std::string&buffer
,因为它只能从构造函数初始化
我可以使用指向std::string
的指针,即std::string*buffer
并将其用作类成员,然后传递s1、s2、s3的地址
class Foo
{
public:
void ProcessString(std::string *buf)
{
// Save pointer
buffer = buf;
// perform operations on std::string
// call other functions within class
// which use same std::string string
}
void Bar()
{
// perform other operations on "std::string" buffer
}
void Baz()
{
// perform other operations on "std::string" buffer
}
private:
std::string *buffer;
};
或者,另一种方法是向每个函数传递对std::string
缓冲区的引用,就像上面第一个示例中所示的
这两种方法似乎都有点难看,因为我很少看到将std::string用作指针或将类的所有函数传递给同一个参数,所以能够使用std::string而不进行复制
有没有更好的办法,或者我现在做的很好?因为你的要求是这样的
1.我不想传递我已经拥有的std::string的副本
2.我不想创建这个类的多个对象
使用pass by ref将是1的解决方案
使用static将是2的解决方案。因为它是一个静态memeber方法,所以该方法只有一个副本。不过,它不会属于任何物体。话虽如此,您可以直接调用此方法,而不是通过对象调用
比如说,
class Foo
{
static void ProcessString(std::string &s)
{
// perform operations on std::string
// call other functions within class
// which use same std::string string
}
}
当您调用此方法时,它将如下所示:
std::string s1, s2, s3;
Foo::ProcessString(s1);
Foo::ProcessString(s2);
Foo::ProcessString(s3);
更进一步,如果您只需要这个类的一个实例,您可以参考singleton设计模式。您基本上需要回答这个问题:谁拥有这个字符串?Foo
是否拥有该字符串?外部调用方是否拥有该字符串?或者他们都拥有这个字符串
“拥有”该字符串意味着该字符串的生存期与其绑定。因此,如果Foo拥有该字符串,那么当Foo停止存在或销毁该字符串时,该字符串将停止存在。共享所有权要复杂得多,但我们可以简单地说,只要任何所有者保留该字符串,该字符串就会存在
每种情况都有不同的答案:
Foo
拥有该字符串:将该字符串复制到Foo
,然后让成员方法对其进行变异
外部资源拥有该字符串:Foo
不应在其自身堆栈之外保留对该字符串的引用,因为该字符串可能会在其不知情的情况下被销毁。这意味着它需要通过引用传递给每个使用它但不拥有它的方法,即使这些方法在同一个类中也是如此
共享所有权:在创建字符串时使用Shared_ptr
,然后将该共享_ptr传递给共享所有权的每个实例。然后将共享的_ptr复制到一个成员变量,并由方法访问它。这比通过引用传递的开销要高得多,但如果您想要共享所有权,这是最安全的方法之一
实际上,还有其他几种方法可以模拟所有权,但它们往往更为深奥。弱所有权、可转让所有权等。在MyObject中保留指向您的对象不拥有的字符串的引用或指针是危险的。很容易出现恶劣的未定义行为
请看以下法律示例(酒吧是公共的):
请看以下内容:
if (is_today) {
myObject.ProcessString(string("Hello")); // uses an automatic temporary string
} // !! end of block: temporary is destroyed!
else {
string tmp = to_string(1234); // create a block variable
myObject.ProcessString(tmp); // call the main function
} // !! end of block: tmp is destroyed
myObject.Bar(); // expects to work with pointer, but in reality use an object that was already destroyed !! => UB
这些错误非常严重,因为在读取函数的使用情况时,一切似乎都正常且管理良好。这个问题被自动销毁集团变量所隐藏
因此,如果确实希望避免字符串的副本,可以按照设想使用指针,但只能在ProcessString()直接调用的函数中使用该指针,并将这些函数设为私有函数
在所有其他情况下,我强烈建议重新考虑您的立场,并设想:
- 应使用字符串的对象中字符串的本地副本李>
- 或者在所有需要它的对象函数中使用
字符串&
参数。这避免了复制,但让调用方负责组织字符串的适当管理李>
您已在未复制的情况下使用该字符串。您是通过引用传递的,即std::string&s。您可能不应该担心字符串复制太多。有什么问题吗?我不明白为什么不通过引用Bar
和Baz
来传递字符串。@ChristianHackl问题主要在于性能,复制对象需要耗费大量CPU和内存,而且在调用者中无法修改对象[如果不完全替换它]除非你通过指针或引用传递。先生,我在noob ages中复制了足够多的字符串,知道它确实会导致性能下降,严重的问题取决于你处理字符串的强度。我知道我在这里谈论的是什么。我希望现在它更清楚。“如果你只想要这个类的一个实例,你可以参考单例设计模式。”如果你只想要这个类的一个实例,这里有一个更简单的模式:Foo f代码>我甚至不认为需要类。为什么不仅仅是一个名称空间
?它可能出于任何原因,这超出了这个问题的范围。总之,代码满足用户的要求@AndreKostur@ChristianHackl不过,我不太清楚你在说什么。如果不使用singleton,这个类的多个实例是可能的。@STNYU:当然,我的评论很讽刺。你会发现现在单身往往被认为是一种反模式;真的
if (is_today) {
myObject.ProcessString(string("Hello")); // uses an automatic temporary string
} // !! end of block: temporary is destroyed!
else {
string tmp = to_string(1234); // create a block variable
myObject.ProcessString(tmp); // call the main function
} // !! end of block: tmp is destroyed
myObject.Bar(); // expects to work with pointer, but in reality use an object that was already destroyed !! => UB