Standard Template Library as container for Objects

M

Michael Winter

Is the Standard Template Library capable of storing complete objects rather
than pointers. For example, would either of the vectors below (a, c)
function correctly (note the member in C).

class A {};
class B {};
class C {
stl::vector<B> b;
};

std::vector<A> a;
std::vector<C> c;

The reason that I ask is that I had something similar to this in a library
that I was writing. It worked as far as I could add objects to the various
containers and index through them. However, when the objects dropped out of
scope and deallocated their lists an assertion was thrown (I forget the
message, but it was from deep within the bowels of CRT new) for each class
object that had a vector containing a class. In release mode, all hell
broke loose (access violations, everywhere).
I know that this would seem to scream, "No, don't use STL containers with
objects!" but I was wondering if there was any other explanation, like using
STL with DLLs, or linking the wrong run-time and using STL, or using
different and incompatible run-times between the test program and the DLL
project. So, could those be the cause, or is it just a case of using
pointers to objects instead? Would that include the stl::auto_ptr template
class?
Are there any rules that should be followed when it comes to the various
run-time libraries? Such as what combinations produce problems, and when
generally should the different libraries be used? Those sorts of details
seemed to be omitted from books, so are there any good references out there
(preferably web-sites, I'm poor, but *really* excellent books are OK).

Thank you (in advance),

Mike
 
A

Attila Feher

Michael said:
Is the Standard Template Library capable of storing complete objects
rather than pointers. For example, would either of the vectors below
(a, c) function correctly (note the member in C).

class A {};
class B {};
class C {
stl::vector<B> b;
};

std::vector<A> a;
std::vector<C> c;

The reason that I ask is that I had something similar to this in a
library that I was writing. It worked as far as I could add objects
to the various containers and index through them. However, when the
objects dropped out of scope and deallocated their lists an assertion
was thrown (I forget the message, but it was from deep within the
bowels of CRT new) for each class object that had a vector containing
a class. In release mode, all hell broke loose (access violations,
everywhere).
I know that this would seem to scream, "No, don't use STL containers
with objects!" but I was wondering if there was any other
explanation, like using STL with DLLs, or linking the wrong run-time
[SNIP]

It was most probably using DLLs. There is some issue (off-topic here) on
Windows if you static-link the runtime to the DLL and the EXE you will get
separate heaps and trouble.

The STL containers are fully capable of storing complete objects and
destroying them properly when they are removed.
 
M

Marijn

Michael Winter said:
Is the Standard Template Library capable of storing complete objects rather
than pointers. For example, would either of the vectors below (a, c)
function correctly (note the member in C).

Basically, storing objects in std:: containers is safe as long as the class
of those objects
support value semantics. Your object will basically need a safe (and
preferably efficient)
default constructor, copy constructor and assignment operator. I suspect
that in the example
you show below you are using a class that does not (properly) implement
these, and that
is what causes the problem at cleanup (double deletes maybe?).

Marijn Haverbeke
 
M

Mike Smith

Attila said:
It was most probably using DLLs. There is some issue (off-topic here) on
Windows if you static-link the runtime to the DLL and the EXE you will get
separate heaps and trouble.

Yes, I find that using the dynamic runtime for all DLLs and EXEs is the
safest practice (and produces the smallest output file). Note that this
means that MSVCPxx.DLL needs to be installed on the target machine if it
is not already present.
 
M

Michael Winter

Thanks for all your help.

Marjin: I didn't specify the copy constructor and assignment operator as
the defaults should suffice, but I'll check. I suppose it's better practice
to explicitly define them anyway.

Attila and Mike: I've posted a question along those lines to a Windows
group, so hopefully they'll provide me some more information.

Again, thank you.

Mike
 
R

Ron Natalie

Marijn said:
Basically, storing objects in std:: containers is safe as long as the class
of those objects
support value semantics. Your object will basically need a safe (and
preferably efficient)
default constructor, copy constructor and assignment operator.

A default constructor is not required.
 
R

Ron Natalie

Michael Winter said:
Thanks for all your help.

Marjin: I didn't specify the copy constructor and assignment operator as
the defaults should suffice, but I'll check. I suppose it's better practice
to explicitly define them anyway.
I don't feel it is. The implementation of these is not trivial to reproduce.
You have to copy/assign each base class and member seperately. If
the compiler generated ones are correct (and if your base classes and
members are all well behaved independentally, it will be).
 
M

Michael Winter

Marjin: I didn't specify the copy constructor and assignment operator
as
I don't feel it is. The implementation of these is not trivial to reproduce.
You have to copy/assign each base class and member seperately. If
the compiler generated ones are correct (and if your base classes and
members are all well behaved independentally, it will be).

I didn't consider large, complex classes. The ones this post relates to
aren't. It's a shame that the we can't have the best of both worlds: have
the compiler worry about simple members and leave the more complicated parts
(deep-copies, for example) for the developer.

Mike
 
R

Ron Natalie

Michael Winter said:
. It's a shame that the we can't have the best of both worlds: have
the compiler worry about simple members and leave the more complicated parts
(deep-copies, for example) for the developer.

Well, it's not clear how you would do that. How do you specify which members/bases
are not automatically copied and which are?

It's workable in C++. If you only have a few things that require special copying
semantics, wrap them in classes that perform those operations. That's one of
the reasons that string is better than char*, and vector is better than handling your
own new'd array, etc...
 
M

Michael Winter

Well, it's not clear how you would do that. How do you specify which
members/bases
are not automatically copied and which are?

That's why I said it's a shame :) You can't in any real sense. Sorry, it
was a pointless comment.
It's workable in C++. If you only have a few things that require special copying
semantics, wrap them in classes that perform those operations. That's one of
the reasons that string is better than char*, and vector is better than handling your
own new'd array, etc...

Mike
 
M

Marijn

Ron Natalie said:
A default constructor is not required.

Well, in some cases it is. If you resize an std::vector it will fill the new
space with default objects, std::map will generate default objects if you
access an unused key value for example. I think that the gcc implementation
(somehow?) complains about a class not having a default constructor when you
create a vector or map with a class that has constructors but no default
constructor.

Marijn
 
?

=?iso-8859-1?Q?Andr=E9_P=F6nitz?=

Michael Winter said:
Thanks for all your help.

Marjin: I didn't specify the copy constructor and assignment operator as
the defaults should suffice, but I'll check. I suppose it's better practice
to explicitly define them anyway.

I doubt so.

Writing them yourself has the potential of missing a member or such,
especially if the class is augmented later in the process.

If the compiler generated constructor and assignment works
conceptually, there is no need at all to try to build equivalent code.

Andre'
 
?

=?iso-8859-1?Q?Andr=E9_P=F6nitz?=

Michael Winter said:
I didn't consider large, complex classes. The ones this post relates to
aren't. It's a shame that the we can't have the best of both worlds: have
the compiler worry about simple members and leave the more complicated parts
(deep-copies, for example) for the developer.

You can put the 'difficult' part in another class with user defined
c'ctor etc and keep the simple stuff as well as an instance of the
'difficult class' as member the your 'real' class.

In fact, if you have to make such a distinction between 'simple' and
'difficult' members, I'd guess that your class is too big already...


Andre'
 
R

Ron Natalie

Marijn said:
Well, in some cases it is. If you resize an std::vector it will fill the new
space with default objects,

Incorrect. It fills the vector with copies of the second argument to resize.
This just happens to be defaulted to a default constructed object of that type.
But nothing precludes you from using an object that can not be default
constructed provided that in the few places where the "fill" object is required
that you provide one rather than relying on the default.

std::map will generate default objects if you access an unused key value for example.

You are correct here. Operator[] has no provision for a non default constructed value_type
(the key_type doesn't matter). However, you're free to use insert rather than operator[]
if you want to use non-default constructable maps.
I think that the gcc implementation
(somehow?) complains about a class not having a default constructor when you
create a vector or map with a class that has constructors but no default
constructor.

If it does it is defective. My version of G++ handles this fine:

#include <vector>
#include <map>
using namespace std;

class NoDefault {
public:
NoDefault(int i);
};

vector<NoDefault> v;
map<int, NoDefault> k1;
map<NoDefault, int> k2;
..
 
M

Marijn

Ron Natalie said:
If it does it is defective. My version of G++ handles this fine:

Hmm i remember having to make a default constructor
for some classes when i did not really need one for anything i did with
them. I'll look into it again, because the way i currently have it -
assert(false) within the default constructor - is really ugly.

Marijn
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top