高效的自定义字符串创建器

Efficient custom string creator

我想知道传递自定义字符串的最有效方法是什么。 例如,我有这个代码段:

outputFile << addSpace(data.len());

其中

string addSpace(int n) {
    string result("");
    for (int i = 0; i < n; i++) {
        result += ' ';
    }
    return result;
}

很明显这个函数不是那么有效,因为字符串是由-val返回的,然后甚至用在了RValue也适合的地方。 如果 N 是固定的,比如 N=5,我可以只使用

outputFile << " "

但显然并非如此。

那么最好的解决方案是什么?(不管这个具体的 N 个空格示例,假设是任何参数相关的字符串创建)。 我考虑过 lambda 函数,但我不太确定。

您可以使用string.insert功能。函数可以从字符串的给定位置开始插入任意数量的字符。

string& insert (size_t pos, size_t n, char c);

参考:http://www.cplusplus.com/reference/string/string/insert/

示例:

string result = "foo";
int n = 5;
result.insert(result.length(), n, ' '); //result will become "foo     "

我认为你的功能并没有那么低效。让我们检查一下潜在的低效率:

  • string::operator+=(char) 看起来很糟糕(对于自定义类型来说可能很糟糕)。但对于字符串,它不会创建和分配新字符串,它实际上只是在现有字符串上附加一个字符;附加一个 char 应该具有恒定的时间复杂度。如果你真的只需要在字符串中重复相同的字符,user2079303 的构造函数建议确实是创建它的正确方法。

  • Return 按值计算的结果: 现代编译器会将 移动 字符串从功能或就地构建它开始,而不是创建临时副本。移动字符串也应该非常快(特别是,没有分配(去)分配动态内存)。

  • 每次调用函数时动态分配一个字符串:这是确实效率低下的一部分。如果您真的需要字符串(并且您的示例只是一个微不足道的例子),按照 user2079303 的建议,提交对预先存在的字符串的引用是解决它的好方法。某些 Java Swing API 已使用接受预先存在的对象引用的函数进行了修改,以避免像您的示例中那样对短期对象进行动态内存操作。

另一种避免创建字符串的方法是重新设计。如果代码确实是关于输出的,请将 ostream 引用传递给函数并让函数决定如何最有效地输出某些内容。正如我所说,这取决于您的用例。

It is clear that the function is not so effective, since the string is returned by-val

字符串是本地字符串,因此按值 return 是唯一的选择。 return 并不是真正使函数效率降低的值,而是您在每次调用中创建一个新字符串的事实。您必须 return 按值这一事实只是该设计的一个症状。然而,缺乏效率可能是微不足道的,除非你多次调用函数,使用不同的 n(如果调用者使用相同的 n,他们可以保留 returned 字符串并重新使用它) .

您当然可以将对字符串的引用传递给函数,让函数修改字符串,然后 return 引用,如果函数是使用相同的参数字符串多次调用,因为这样可以避免每次调用都创建一个新字符串。但是随后您需要在调用代码中管理该外部字符串,这会使该函数的使用更加复杂,从而变得更糟。而且,如果您不重用参数字符串,这也不会更有效。

如果您选择为每个实例使用一个新字符串,则有一种比您的函数更简单(并且可能更有效,但甚至比重新使用字符串的差异更小)的方法。只需使用 std::string:

的构造函数
outputFile << std::string(data.len(), ' ');

对于这种特殊情况,根本不创建字符串可能更有效:

outputFile << std::setfill(' ') << std::setw(data.len()) << ' ';

所以对于一般情况,选择归结为:效率比漂亮的界面更重要,字符串可以重用 - 然后传递对字符串的引用并修改。否则 return 在您的示例中使用新字符串会更好。

您可以 return 包含足够信息的东西来执行您的 io 操作


在这种情况下,您的 addspace 可以变成

#include <iostream>

struct secret_thing_to_add_space{
   int number;
};

std::ostream& operator << (std::ostream& os, const secret_thing_to_add_space& s){
   //just use simple method here, you can use something different
   for(int i=0;i<s.number;++i)os<<' ';
   return os;
};

secret_thing_to_add_space addspace(int n){
   return {n};
}

int main(){
   std::cout<< "begin" << addspace(10) << "end";
}

如您所见,它变得复杂了。 (在这种情况下不太可能更有效率)