如何在不调用复制构造函数的情况下使用类初始化STL向量/列表

时间:2010-04-05 18:26:03

标签: c++ list stl vector

我有一个C ++程序,它使用包含类实例的std :: list。如果我打电话给myList.push_back(MyClass(variable));它经历了创建临时变量的过程,然后立即将其复制到向量,然后删除临时变量。这并不像我想要的那么高效,当你需要深层复制时很糟糕。

我很想拥有我的类new的构造函数,而不必实现复制构造函数,只是为了第二次分配我的内存并浪费运行时。我也不必立即从向量/列表中找到类实例,然后手动分配内存(或做一些可怕的事情,比如在复制构造函数本身中分配内存)。

有没有办法(我不使用Visual Studio BTW)?

7 个答案:

答案 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_ptrshared_array来管理您的课程要分配的内存。然后,编译器提供的复制构造函数将简单地增加引用计数,因为shared_ptr复制自身。对于标准容器而言,这是一个重要的使用概念,您的元素可以便宜地复制。标准库可以在任何地方进行复制。

答案 6 :(得分:0)

我建议使用std::vector<std::unique_ptr>,因为它会在需要时自动释放内存,其开销低于std::shared_ptr。唯一需要注意的是,这个指针没有引用计数,所以你必须小心不要将指针本身复制到其他地方,以免数据在其他地方被使用时被删除。