Container of objects or container of pointers?

Started by
5 comments, last by SnotBob 15 years, 10 months ago
I'm wondering if I should always use containers of pointers or just plain containers when the objects aren't extremely small. An example case: The object X contains a vector, which is filled with data by the constructor of X, and after that it is left unchanged for the entire lifetime of X, but is read frequently. The exact ratio between creation and read operations varies a lot. Example of using "vector of objects":

class X {
  ...
  std::vector<Foo> v;
};

X::X(some_parameters...) {
  v.resize(n);
  for(int i=0; i<n; ++i) {
    v.push_back(Foo(some_parameters...));
  }
}

const Foo& X::getData(some_parameters...) {
  return v[some_index...];
}
Example of using "vector of pointers":

class X {
  ...
  std::vector<Foo *> v;
};

X::X(some_parameters...) {
  v.resize(n);
  for(int i=0; i<n; ++i) {
    v.push_back(new Foo(some_parameters...));
  }
}

const Foo& X::getData(some_parameters...) {
  return *v[some_index...];
}
The question is whether, in the first case, the compiler will be able to optimize away the copy constructor and destructor (or assignment operator) calls for the object passed to push_back, even if the Foo object has custom-written copy constructor, destructor and assignment operator (then, presumably, we will also save calls to new and delete for each Foo object and so save a lot of performance compared to the 2nd example for ALL cases). Or, will the second way of writing it become faster when the Foo objects get larger and more complex?
Advertisement
Well your points are valid and I can't tell you off the top of my head which is faster. Just write a quick timed test.
However, I would use whichever is nicer design wise. I personally use the stack version for small objects, for me only structs, and I use the heap version for larger classes. This way the small objects are used by value and only the pointer of the large ojects is accessed. This prevents the use of the & symbol to acquire an address later on in the code whic makes things nicer IMHO.

Quote:Original post by all_names_taken
The question is whether, in the first case, the compiler will be able to optimize away the copy constructor and destructor (or assignment operator) calls for the object passed to push_back

Why don't you just put
cout << "copy constructor" << endl;

inside of the copy constructor and see for yourself?
Hi,

I wouldn t worry too much about the speed of the 2 versions.

The choice also depends on the container type you are using.

For version 1) inserting right into a vector requires you to move the allocated objects fore and back.
On the other hand you could say, using a vector with repeated insertion/deletion is bad practice and should be avoided

Using a list instead only requires the allocation of the iterator type.


I would only use version 2) if you want to pass the addresses of you objects to other objects to store them (e.g.: in a manager class).
Consider smart pointers here to get rid of the deletion code.

If you have to insert or delete objects frequently consider using a list instead.
If your objects are that large that reallocation is expensive(no POD type), then they are complex enough, such that you usually don t need them to be continuously aligned in memory.
http://www.8ung.at/basiror/theironcross.html
Quote:Original post by DevFred
Quote:Original post by all_names_taken
The question is whether, in the first case, the compiler will be able to optimize away the copy constructor and destructor (or assignment operator) calls for the object passed to push_back

Why don't you just put
cout << "copy constructor" << endl;

inside of the copy constructor and see for yourself?

That would prove nothing because the language semantics call for the copy constructor to be executed. The compiler can effectively do away with the copy constructor if it's inlined, but the semantics of the program must remain the same.

To the original poster, you should be primarily concerned about the design. Since your class has a copy constructor, you probably intend for the objects to be copied, rather than passed around through pointers. So it makes more sense to have objects in the vector instead of pointers. If you believe there's a performance issue, you should measure it both ways after profiling.
Don't forget that the new operator (and your missing delete operator) are very expensive operations. You need to compare the cost of dynamic allocation with the cost of your Foo copy constructor. Also, consider the cost of object constrction -- it may be greater than object copying.

It's also quite possible that the compiler would elide the copy constructor when an object in a vector is constructed in place such as in your code example. That's an implementation-specific detail, but it explicitly allowed by the standard, so it's possible.

Really, there is no answer in the general case. Only direct measurement will give you an answer in a particular case.

Stephen M. Webb
Professional Free Software Developer

Quote:Original post by Bregma
It's also quite possible that the compiler would elide the copy constructor when an object in a vector is constructed in place such as in your code example. That's an implementation-specific detail, but it explicitly allowed by the standard, so it's possible.

The standard allows in-place construction of function arguments instead of creation and copying of a temporary. However, I do not believe that copy construction can be avoided in the vector<>::push_back() function itself, because it's explicitly invoked by a placement new. The standard specifically says that a new expression with an initializer calls the appropriate constructor.

This topic is closed to new replies.

Advertisement