8

Please consider the following:

class CMyClass
{
public:
  CMyClass()
  {
    printf( "Constructor\n" );
  }
  CMyClass( const CMyClass& )
  {
    printf( "Copy constructor\n" );
  }
};

int main()
{
  std::list<CMyClass> listMyClass;

  listMyClass.resize( 1 );

  return 0;
}

It produces the following output:

Constructor

Copy constructor

Now my question is: How do I avoid the copy constructor? Or to put it in another way: How can I create objects inside an STL container without the unnecessary copy operation. Is there some way to do an "in-place" construction using the default constructor?


Update - answers so far:

  1. It can't be done
  2. Use pointers or smart pointers instead.

Smart pointers are an overkill for my application. But I really wonder why this can't be done. It seems like such an obvious thing to want to do. Any other ideas? I will even accept a nasty hack if it works...


Solution

I think I just found a solution for my problem from all the comments and answers posed here. The solution is to construct an empty object and to keep it around for the sole purpose of using it later for making clean copies of. Then you can use one of the methods that take a reference (like push_back or insert). This still calls the copy constructor for every new object inserted, but at least it is not both the default constructor AND copy constructor:

int main()
{
  CMyClass Empty;

  std::list<CMyClass> listMyClass;

  for ( int c=0; c<10; ++c )
  {
    listMyClass.push_back( Empty );
  }

  return 0;
}
Community
  • 1
  • 1
Barnett
  • 1,491
  • 12
  • 18
  • It might seem an obvious thing, but from a functional point of view, it doesn't matter and several algorithms (sort, remove,...) also use copying, so be prepared to be copied ;) (See also http://www.devx.com/tips/Tip/13606) – stefaanv Jan 20 '10 at 10:41
  • An obvious thing to do? Why is that? Why is copying a problem? – jalf Jan 20 '10 at 11:11
  • Obvious? If you called `mylist.resize(10)`, how would you expect to end up with ten distinct objects if they are not copies? – visitor Jan 20 '10 at 11:26
  • All I want to do is to is to construct a new object inside a list. What STL does is to construct a temp object first and then copy the temp object using the copy constructor. This copy is an unnecessary waste of time. An alternative implementation of mylist.resize(10) could have called the default constructor 10 times. What it does instead is to call the default constructor once, followed by the copy constructor 10 times. This is a small overhead for 10 objects (10%), but if you have to add the objects one at a time, the overhead is double (100%). – Barnett Jan 20 '10 at 11:45
  • Well, you may wish the standard container behaved like that, but they don't. In some cases, the call to the copy ctor may be elided by the compiler, but if you don't want to depend on it, why not use pointers, like everyone is suggesting? –  Jan 20 '10 at 12:01
  • This is not an application for a smart pointer. It just won't be elegant and introduce even more overhead. The list container is already providing all the "smartness" I need. ;-) Anyway thanks for all the help everyone. I found a solution that will work, as explained above. – Barnett Jan 20 '10 at 12:28
  • To me it seems like a microoptimization. Are you sure that you have problems there? Also note that in most containers you do not need to resize to add new elements, but rather 'push_back' at the end of the current container. – David Rodríguez - dribeas Jan 20 '10 at 12:52
  • The list is used as a fifo buffer for a data stream, so the optimization is justified. Yes, push_back() is used in the code - I just used resize() here as an example of an function that I think can benefit from having an implementation that does not always do a copy operation. – Barnett Jan 20 '10 at 13:40
  • You should check out this question: http://stackoverflow.com/questions/1262808/which-stl-container-should-i-use-for-a-fifo The top answer is good reading. Also check out the Boost circular buffer library: http://www.boost.org/doc/libs/1_41_0/libs/circular_buffer/doc/circular_buffer.html – Emile Cormier Jan 22 '10 at 04:09

7 Answers7

8

By design, all the C++ Standard Library containers store copies. Therefore the call to the copy constructor cannot be avoided if you wish to store values in the container - the only way out is to store pointers instead. If you want to mitigate the overhead of copying, investigate the use of reference counting.

2

Sorry, not currently with std::list and similar containers. (But you can write your own slightly-different container if you really need this, and still follow the rest of the STL interface.)

You don't have to use the default ctor, however:

std::list<CMyClass> listMyClass;
listMyClass.resize(1, obj_to_copy_from);

Such as:

std::list<CMyClass> listMyClass;
listMyClass.resize(1, CMyClass(use, specific, ctor));

Resize looks like:

void list<T>::resize(size_type new_size, T copy_from = T());

Which creates a new object (using the default ctor) by default.

1

use pointers

std::list<CMyClass*> listMyClass;
Yin Zhu
  • 16,980
  • 13
  • 75
  • 117
1

It would be useful if vector allowed initializing new elements with default constructor on resize because of performance, but this is not supported. If you really need this, implement custom container - there is no other way.

Nikola Mihajlović
  • 2,286
  • 2
  • 19
  • 23
0

use a pointer or smart pointer (boost::shared_ptr and not auto_ptr)

Prashant
  • 68
  • 6
0

you can do in c11, for older versions, vesry simple can be improved

vector

myclas: public vector<A>
{
    push_back(Aconstructor_parameters)
    {
       new(_Mylast++) A(Aconstructor_parameters);
    }
};

when using ensure that you have used reserve() to allocate the memory

Sagar Pudi
  • 4,634
  • 3
  • 32
  • 51
sfsdf
  • 1
0

http://swagatata.tumblr.com/post/5896332725/vector-construction

Even if you do

std::list<classname> objectname(100);

the constructor would be called once and the 100 objects would be constructed using copy constructor.

user487478
  • 371
  • 4
  • 6