我见过我的同事经常做第二个片段。为什么是这样?我已经尝试添加打印语句来跟踪ctors和dtors,但两者看起来完全相同。
std::vector<ClassTest> vecClass1;
ClassTest ct1;
ct1.blah = blah // set some stuff
...
vecClass1.push_back(ct1);
std::vector<ClassTest> vecClass2;
vecClass2.push_back(ClassTest());
ClassTest& ct2 = vecClass2.back();
ct2.blah = blah // set some stuff
...
PS。如果标题有误导性,我很抱歉。
修改
首先,谢谢大家的回复。
我使用std::move
编写了一个小应用程序。结果让我感到惊讶,也许是因为我做错了...有人会解释为什么“快速”路径表现得更好。
#include <vector>
#include <string>
#include <boost/progress.hpp>
#include <iostream>
const std::size_t SIZE = 10*100*100*100;
//const std::size_t SIZE = 1;
const bool log = (SIZE == 1);
struct SomeType {
std::string who;
std::string bio;
SomeType() {
if (log) std::cout << "SomeType()" << std::endl;
}
SomeType(const SomeType& other) {
if (log) std::cout << "SomeType(const SomeType&)" << std::endl;
//this->who.swap(other.who);
//this->bio.swap(other.bio);
this->who = other.who;
this->bio = other.bio;
}
SomeType& operator=(SomeType& other) {
if (log) std::cout << "SomeType::operator=()" << std::endl;
this->who.swap(other.who);
this->bio.swap(other.bio);
return *this;
}
~SomeType() {
if (log) std::cout << "~SomeType()" << std::endl;
}
void swap(SomeType& other) {
if (log) std::cout << "Swapping" << std::endl;
this->who.swap(other.who);
this->bio.swap(other.bio);
}
// move semantics
SomeType(SomeType&& other) :
who(std::move(other.who))
, bio(std::move(other.bio)) {
if (log) std::cout << "SomeType(SomeType&&)" << std::endl;
}
SomeType& operator=(SomeType&& other) {
if (log) std::cout << "SomeType::operator=(SomeType&&)" << std::endl;
this->who = std::move(other.who);
this->bio = std::move(other.bio);
return *this;
}
};
int main(int argc, char** argv) {
{
boost::progress_timer time_taken;
std::vector<SomeType> store;
std::cout << "Timing \"slow\" path" << std::endl;
for (std::size_t i = 0; i < SIZE; ++i) {
SomeType some;
some.who = "bruce banner the hulk";
some.bio = "you do not want to see me angry";
//store.push_back(SomeType());
//store.back().swap(some);
store.push_back(std::move(some));
}
}
{
boost::progress_timer time_taken;
std::vector<SomeType> store;
std::cout << "Timing \"fast\" path" << std::endl;
for (std::size_t i = 0; i < SIZE; ++i) {
store.push_back(SomeType());
SomeType& some = store.back();
some.who = "bruce banner the hulk";
some.bio = "you do not want to see me angry";
}
}
return 0;
}
输出:
dev@ubuntu-10:~/Desktop/perf_test$ g++ -Wall -O3 push_back-test.cpp -std=c++0x
dev@ubuntu-10:~/Desktop/perf_test$ ./a.out
Timing "slow" path
3.36 s
Timing "fast" path
3.08 s
答案 0 :(得分:7)
如果在“设置一些东西”后复制对象比以前更昂贵,那么当你在“设置一些东西”之前插入对象时,将对象插入向量时发生的复制会更便宜后。
但是,真的,因为你应该期望偶尔复制矢量中的对象,这可能不是一个优化。
答案 1 :(得分:3)
如果我们接受你的同事的片段是明智的,因为ClassTest复制起来很昂贵,我宁愿:
using std::swap;
std::vector<ClassTest> vecClass1;
ClassTest ct1;
ct1.blah = blah // set some stuff
...
vecClass1.push_back(ClassTest());
swap(ct1, vecClass1.back());
我认为它更清晰,而且可能更安全。 ...
代码可能会分配资源,因此可能会抛出异常(或者是什么使得完全构建的ClassTest
复制如此昂贵?)。因此,除非向量确实是函数的本地向量,否则我认为在运行该代码时将其作为半构造是个好主意。
如果ClassTest
只有默认的swap
实施,但如果ClassTest
没有效率swap
,那么这会更加昂贵,那么它就没有了复制的业务很昂贵。所以这个技巧也许应该只用于已知友好的类,而不是未知的模板参数类型。
正如Gene所说,如果你有C ++ 0x功能,std::move
无论如何都会更好。
如果我们担心ClassTest复制成本很高,那么重新定位矢量是一个可怕的前景。所以我们也应该:
deque
代替vector
。答案 2 :(得分:2)
第二个版本受益于移动临时版。第一个版本是复制临时向量。所以第二个可能更快。第二个版本也有可能更小的峰值内存要求,第一个版本创建两个对象一个临时和一个副本,然后删除临时。您可以通过显式移动临时版来改进第一个版本:
std::vector<ClassTest> vecClass1;
ClassTest ct1;
ct1.blah = blah // set some stuff
...
vecClass1.push_back(std::move(ct1));
答案 3 :(得分:1)
你应该让你的同事知道完全为什么,但我们仍然可以猜测。正如詹姆斯指出的那样,如果一旦构造完成复制对象的成本更高,那么效率可能会提高一些。
我看到两个版本都有优势。
我喜欢你的同事的片段因为:虽然在这两种情况下都有2个对象,但它们只在第二个版本中共存很短的时间。只有一个对象可供编辑:这可以避免在ct1
之后编辑push_back
的潜在错误。
我喜欢您的个人代码段,因为:调用push_back
添加第二个对象可能使引用ct2
无效,从而导致未定义的风险行为。第一个片段不存在这种风险。
答案 4 :(得分:0)
它们完全相同(据我所知)。也许他或她这样做是惯用的习惯。