我有一个C ++程序,它使用包含类实例的std :: list。如果我打电话给myList.push_back(MyClass(variable));
它经历了创建临时变量的过程,然后立即将其复制到向量,然后删除临时变量。这并不像我想要的那么高效,当你需要深层复制时很糟糕。
我很想拥有我的类new
的构造函数,而不必实现复制构造函数,只是为了第二次分配我的内存并浪费运行时。我也不必立即从向量/列表中找到类实例,然后手动分配内存(或做一些可怕的事情,比如在复制构造函数本身中分配内存)。
有没有办法(我不使用Visual Studio BTW)?
答案 0 :(得分:9)
Ahem。为了科学的利益,我提出了一个很小的测试程序来检查编译器是否省略了副本:
#include <iostream>
#include <list>
using namespace std;
class Test
{
public:
Test() { cout<<"Construct\n"; }
Test(const Test& other) { cout<<"Copy\n"; }
Test& operator=(const Test& other) { cout<<"Assign\n"; return (*this); }
};
Test rvo() { return Test(); }
int main()
{
cout<<"Testing rvo:\n";
Test t = rvo();
cout<<"Testing list insert:\n";
list<Test> l;
l.push_back(Test());
}
这是我在MSVC ++ 2008上的输出:
Testing rvo: Construct Testing list insert: Construct Copy
调试和发布版本都是一样的:RVO工作,临时对象传递没有优化 如果我没有弄错的话,在C ++ 0x标准中添加的Rvalue references旨在解决这个问题。
答案 1 :(得分:9)
C ++ 0x移动构造函数是一个部分解决方法:而不是被调用的复制构造函数,移动构造函数将是。移动构造函数就像复制构造函数,但允许使源参数无效。
C ++ 0x添加另一个功能,它可以完全按照您的要求执行:emplace_back
。 (N3092§23.2.3)你将参数传递给构造函数,然后用这些参数(通过...
和转发)调用构造函数,这样就不能调用其他构造函数了。
对于C ++ 03,您唯一的选择是为您的班级添加未初始化状态。在push_back
之后立即调用的另一个函数中执行实际构造。 boost::optional
可能会帮助您避免初始化类的成员,但它反过来要求他们是可复制构造的。或者,正如弗雷德所说,用最初空的智能指针完成同样的事情。
答案 2 :(得分:5)
C ++ 0x移动构造函数(可用于VC ++ 2010和最近的GNU编译器)正是您正在寻找的。
答案 3 :(得分:4)
事实上,在这种情况下,编译器可能会忽略副本。
如果你的编译器没有这样做,一种避免复制的方法是让你的列表包含指针而不是实例。您可以使用智能指针为您清理对象。
答案 4 :(得分:2)
查看Boost的ptr_container库。我特别使用ptr_vector:
boost::ptr_vector<Foo> c;
c.push_back(new Foo(1,2,3) );
c[0].doSomething()
当它超出范围时,将在向量的每个元素上调用delete
。
答案 5 :(得分:1)
使用shared_ptr
或shared_array
来管理您的课程要分配的内存。然后,编译器提供的复制构造函数将简单地增加引用计数,因为shared_ptr
复制自身。对于标准容器而言,这是一个重要的使用概念,您的元素可以便宜地复制。标准库可以在任何地方进行复制。
答案 6 :(得分:0)
我建议使用std::vector<std::unique_ptr>
,因为它会在需要时自动释放内存,其开销低于std::shared_ptr
。唯一需要注意的是,这个指针没有引用计数,所以你必须小心不要将指针本身复制到其他地方,以免数据在其他地方被使用时被删除。