我有一个类的模拟代码,它使用一个属性来设置文件名:
#include <iostream>
#include <iomanip>
#include <sstream>
class Test {
public:
Test() { id_ = 1; }
/* Code which modifies ID */
void save() {
std::string filename ("file_");
filename += getID();
std::cout << "Saving into: " << filename <<'\n';
}
private:
const std::string getID() {
std::ostringstream oss;
oss << std::setw(4) << std::setfill('0') << id_;
return oss.str();
}
int id_;
};
int main () {
Test t;
t.save();
}
我关心的是getID
方法。乍一看,由于我正在创建ostringstream
及其相应的string
以返回,因此效率似乎非常低效。我的问题:
1)因为它返回const std::string
是编译器(在我的情况下是GCC)能够优化它吗?
2)有没有办法改善代码的性能?也许移动语义或类似的东西?
谢谢!
答案 0 :(得分:1)
1)因为它返回const std :: string是编译器(在我的情况下是GCC) 能够优化吗?
返回const对象没有意义,除非通过引用返回
2)有没有办法改善代码的性能?也许搬家吧 语义或类似的东西?
Id id_
不会改变,只需在构造函数中创建值,使用静态方法可能有所帮助:
static std::string format_id(int id) {
std::ostringstream oss;
oss << std::setw(4) << std::setfill('0') << id;
return oss.str();
}
然后:
Test::Test()
: id_(1)
, id_str_(format_id(id_))
{ }
<强>更新强>
这个问题对于这个问题并不完全有效,因为id_
确实发生了变化,我不会删除它,因为也许有人会发现它对他的案例有用。无论如何,我想澄清一些想法:
我的建议
一个选项是随时更新id_str_
字段id_
更改(必须设置id_
的设置者),因为您已经已经改变成员id_
我认为没有问题更新另一个。
这种方法允许将getID()
实现为一个简单的getter(应该是const,btw),没有性能问题,字符串字段只计算一次。
答案 1 :(得分:1)
在打开文件之类的昂贵操作之前创建ostringstream
一次,对您的程序的效率并不重要,所以不要担心它
但是,您应该担心代码中出现的一个坏习惯。值得赞扬的是,您似乎已经确定了它:
1)因为它返回
const std::string
是编译器(在我的情况下是GCC)能够优化它吗?2)有没有办法改善代码的性能?也许移动语义或类似的东西?
是。考虑:
class Test {
// ...
const std::string getID();
};
int main() {
std::string x;
Test t;
x = t.getID(); // HERE
}
在标记为// HERE
的行上,调用了哪个赋值运算符?我们想调用移动赋值运算符,但该运算符原型为
string& operator=(string&&);
我们实际传递给我们的 operator=
的论点属于&#34;对const string
&#34;类型的右值的引用 - 即const string&&
。 const-correctness规则阻止我们将const string&&
静默地转换为string&&
,因此当编译器创建一组赋值运算符函数时,它可以在这里使用(< em>重载集),它必须排除采用string&&
的移动赋值运算符。
因此,x = t.getID();
最终会调用 副本 - 分配运算符(因为const string&&
可以安全地转换为const string&
)如果只有你没有陷入const
的坏习惯 - 你的退货类型合格,你就可以制作一份本可以避免的额外副本。
当然,getID()
成员函数应该声明为const
,因为它不需要修改*this
对象。
所以正确的原型是:
class Test {
// ...
std::string getID() const;
};
经验法则是: 始终按值返回,并且永远不会按const
值返回。
答案 2 :(得分:0)
您可以通过以下方式更改getID
:
std::string getID() {
thread_local std::ostringstream oss;
oss.str(""); // replaces the input data with the given string
oss.clear(); // resets the error flags
oss << std::setw(4) << std::setfill('0') << id_;
return oss.str();
}
每次都不会创建新的ostringstream
。
在你的情况下它是不值得的(因为Chris Dodd说打开一个文件并写入它可能要贵10-100倍)......只是要知道。
还要考虑在任何合理的库实现中std::to_string
至少与stringstream
一样快。
1)因为它返回const std :: string是编译器(在我的情况下是GCC) 能够优化吗?
这种做法有一个基本原理,但它基本上是过时的(例如Herb Sutter建议为非原始类型返回const值。)
对于C ++ 11,强烈建议将值返回为非const,以便您可以充分利用 rvalue 引用。
关于此主题,您可以查看:
答案 3 :(得分:0)
一种可能性是做这样的事情:
std::string getID(int id) {
std::string ret(4, '0') = std::to_string(id);
return ret.substring(ret.length()-4);
}
如果您正在使用包含短字符串优化(例如,VC ++)的实现,那么很可能会大大提高速度(VC ++的快速测试显示它大约为4-5倍快)。
OTOH,如果您正在使用不包含短字符串优化的实现,那么很可能它会基本上更慢。例如,使用g ++运行相同的测试,会产生大约慢4-5倍的代码。
还有一点:如果你的ID号可能超过4位数,这并没有给出相同的行为 - 它总是返回一个正好4个字符的字符串而不是stringstream
代码创建的最少4个。如果你的身份证号码可能超过9999,那么这段代码根本不适合你。