CList<CString> Split(char delim) {
CList<CString> subStrings;
for (char* ps = Buffer(); *ps; ps++) {
char* pe;
for (pe = ps; *pe && (*pe != delim); pe++)
;
subStrings.Append (CString(ps, int (pe - ps)));
if (!*pe)
break;
ps = pe;
}
return subStrings;
}
CList<CString> values = s.Split(';');
最后的语句导致列表的复制构造函数被调用两次:第一次是当 subString 从方法中传递出去时。 Split() 本地堆栈上的实例被复制(复制到只有编译器知道的位置)以持续足够长的时间,以便再次复制到“值”。
如果不从根本上更改我的代码(例如向 Split() 添加 CList& 参数),我将如何在此处不进行两个(相当昂贵的)列表复制操作?
编辑:
编译器标志设置为使用 c++20。
编辑2:
小型测试项目。
https://drive.google.com/file/d/1f1GVRJ9lbu2nYnhsLLv3YbCjtKr8m6-k/view?usp=sharing
最佳答案
我使用此代码(使用 Visual Studio 2019)运行了一些测试,并且观察到调试和发布版本中的行为差异。调试版本会调用复制构造函数,并且不会执行 RVO/复制省略。发布版本会进行优化,并且不会进行不必要的复制。此示例向您展示如何以最少的复制来拆分字符串(在发布版本中)
这个结果确实让我有些惊讶,所以感谢您提出这个问题。我今天确实学到了一些东西:)
发布版本的输出符合预期(emplace_back 也不复制):
-----------------------------------------------------
calling split function
my_list::my_list
my_string::my_string
my_string::my_string
my_string::my_string
my_string::my_string
-----------------------------------------------------
result of split
the
quick
brown
fox
-----------------------------------------------------
cleanup vector starting
my_list::~my_list
my_string::~my_string
my_string::~my_string
my_string::~my_string
my_string::~my_string
但是调试版本显然没有进行这种优化:
my_list::my_list
my_string::my_string
my_string::my_string
my_string::my_string
my_string::my_string
my_string::my_string(const my_string&), copy constructor
my_string::my_string(const my_string&), copy constructor
my_string::my_string(const my_string&), copy constructor
my_string::my_string(const my_string&), copy constructor
my_list::my_list(const my_list&), copy constructor
my_list::~my_list
my_string::~my_string
my_string::~my_string
my_string::~my_string
my_string::~my_string
-----------------------------------------------------
result of split
the
quick
brown
fox
-----------------------------------------------------
cleanup vector starting
my_list::~my_list
my_string::~my_string
my_string::~my_string
my_string::~my_string
my_string::~my_string
这是测试代码:
#include <iostream>
#include <vector>
struct my_string
{
my_string(const char* from, const char* to) :
value(from, to)
{
std::cout << " my_string::my_string\n";
}
my_string(const my_string& rhs) :
value{ rhs.value }
{
std::cout << " my_string::my_string(const my_string&), copy constructor\n";
}
my_string(my_string&& rhs) :
value{ std::move(rhs.value) }
{
std::cout << " my_string::my_string(my_string&&), move constructor\n";
}
~my_string()
{
std::cout << " my_string::~my_string\n";
}
std::string value;
};
struct my_list
{
my_list()
{
// note reserving some more room up front will reduce reallocations
// try commenting this out and you will see many more strings created/destroyed
// because of vector reallocation
strings.reserve(128);
std::cout << "my_list::my_list\n";
}
my_list(const my_list& rhs) :
strings{ rhs.strings }
{
std::cout << "my_list::my_list(const my_list&), copy constructor\n";
}
~my_list()
{
std::cout << "my_list::~my_list\n";
}
std::vector<my_string> strings;
};
my_list split(const char* string, char delim)
{
my_list list;
for (const char* ps = string; *ps != 0; ++ps)
{
const char* pe{ ps };
while ((*pe != 0) && (*pe != delim))
{
++pe;
}
list.strings.emplace_back(ps,pe);
ps = pe;
}
return list;
}
int main()
{
std::cout << "-----------------------------------------------------\n";
std::cout << "calling spit function\n\n";
{
auto mylist = split("the,quick,brown,fox", ',');
std::cout << "-----------------------------------------------------\n";
std::cout << "result of split \n\n";
for (const auto& mystring : mylist.strings)
{
std::cout << mystring.value << "\n";
}
std::cout << "\n-----------------------------------------------------\n";
std::cout << "cleanup vector starting";
}
}
关于c++ - 在类方法中有效地创建一个类并将其从那里传递出去?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69391661/