简体   繁体   中英

How to initialise a STL vector/list with a class without invoking the copy constructor

I have a C++ program that uses a std::list containing instances of a class. If I call eg myList.push_back(MyClass(variable)); it goes through the process of creating a temporary variable, and then immediately copies it to the vector, and afterwards deletes the temporary variable. This is not nearly as efficient as I want, and sucks when you need a deep copy.

I would love to have the constructor of my class new something and not have to implement a copy constructor just to allocate my memory for the second time and waste runtime. I'd also rather not have to immediately find the class instance from the vector/list and then manually allocate the memory (or do something horrible like allocate the memory in the copy constructor itself).

Is there any way around this (I'm not using Visual Studio BTW)?

Ahem. In the interests of science , I've whipped up a tiny test program to check whether the compiler elides the copy or not:

#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());
}

And here's my output on MSVC++2008:

Testing rvo:
Construct 
Testing list insert:
Construct
Copy

It's the same for both debug and release builds: RVO works, temporary object passing isn't optimized.
If I'm not mistaken, the Rvalue references being added in the C++0x standard are intended to solve this very problem.

C++0x move constructors are a partial workaround: instead of the copy constructor being invoked, the move constructor would be. The move constructor is like the copy constructor except it's allowed to invalidate the source argument.

C++0x adds another feature which would do exactly what you want: emplace_back . (N3092 §23.2.3) You pass it the arguments to the constructor, then it calls the constructor with those arguments (by ... and forwarding) so no other constructor can ever be invoked.

As for C++03, your only option is to add an uninitialized state to your class. Perform actual construction in another function called immediately after push_back . boost::optional might help you avoid initializing members of the class, but it in turn requires they be copy-constructible. Or, as Fred says, accomplish the same thing with initially-empty smart pointers.

C ++ 0x移动构造函数(可用于VC ++ 2010和最近的GNU编译器)正是您正在寻找的。

In fact, the compiler might elide the copy in this case.

If your compiler doesn't do this, one way to avoid copying would be to have your list contain pointers instead of instances. You could use smart pointers to clean up the objects for you.

Check out Boost's ptr_container library. I use the ptr_vector in particular:

boost::ptr_vector<Foo> c;
c.push_back(new Foo(1,2,3) );
c[0].doSomething()

and when it goes out of scope, delete will be called on each element of the vector.

Use a shared_ptr or shared_array to manage the memory your class wants to allocate. Then the compiler-provided copy-constructor will simply increment a reference count as the shared_ptr copies itself. It's an important usage concept for standard containers that your elements be cheap to copy. The standard library makes copies all over the place.

I would suggest using an std::vector<std::unique_ptr> , because it automatically releases the memory when needed, with less overhead than std::shared_ptr . The only caveat is that this pointer doesn't have reference counting, and so you have to be careful not to copy the pointer itself somewhere else, lest the data be deleted when it is still used somewhere else.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM