Which is best way to store objects?

A

Angus

Hello

I want to store a collection of objects in a vector. I think the best
way is for me to create each object on the free store (heap) and then
store a pointer to the object in the vector.

Eg like this:
std::vector<MyWidget*> m_widgets;
MyWidget* newone = new MyWidget;
//populate/process
m_widgets.push_back(newone);

Rather than:
std::vector<MyWidget> m_widgets;
MyWidget newone;
//populate/process
m_widgets.push_back(newone);

Reason being that with a pointer the push back does not have to do a
copy of the entire object, it simply copies the address of the object.

The only downside to pointer approach is having to delete the object
at the end but that is easy enough to do.

What other approaches could I use? Could I use a reference instead?

Any comments would be very much appreciated.

Angus
 
J

Jonathan Lee

Hello

I want to store a collection of objects in a vector.  I think the best
way is for me to create each object on the free store (heap) and then
store a pointer to the object in the vector.

I think the best option for what you describe would be to use a
smart pointer, like the Boost shared_ptr.
Reason being that with a pointer the push back does not have to do a
copy of the entire object, it simply copies the address of the object.

Depending on your object, you might have the option of pushing an
"empty" object and then using swap to get your data into the vector.

myvector.push_back(MyObject()); // Default constructor
myvector[myvector.size() - 1].swap(TheActualObject);

(Presumably the default constructor has very little data in it.)
A little roundabout, but you get to have a vector of objects instead
of a vector of pointers.

--Jonathan
 
J

James Kanze

I want to store a collection of objects in a vector. I think
the best way is for me to create each object on the free store
(heap) and then store a pointer to the object in the vector.

What are the semantics of the object? If they're value, then
you shouldn't allocate on the heap, but rather copy. (And if
they aren't value, your collection is logically a collection of
pointers to the object, so it doesn't matter.)
Eg like this:
std::vector<MyWidget*> m_widgets;
MyWidget* newone = new MyWidget;
//populate/process
m_widgets.push_back(newone);
Rather than:
std::vector<MyWidget> m_widgets;
MyWidget newone;
//populate/process
m_widgets.push_back(newone);
Reason being that with a pointer the push back does not have
to do a copy of the entire object, it simply copies the
address of the object.

Big deal. If the profilers shows this to be a bottle neck, you
can consider using pointers. (Normally, I'd look to making
copying faster first, but it's not always that simple.) But
otherwise, all you're doing is creating confusion.
The only downside to pointer approach is having to delete the
object at the end but that is easy enough to do.

There's no real downside nor upside. A container of pointers
has radically different semantics than a container of the
objects themselves, and you can't simply substitute one for the
other. If your object has value semantics, a container of
pointers to it is an error, and doesn't work without a lot of
hassle on the user side. If your object has entity semantics,
then you can't put it in a container (since it cannot be copied,
at least not externally); any containers will use pointers.
(BTW: if your Widget refers to a graphic Widget, it probably has
identity, and can't be copied or put into a container.)
 
J

James Kanze

I think the best option for what you describe would be to use
a smart pointer, like the Boost shared_ptr.

For what use? In this regard, boost::shared_ptr has pointer
semantics (exactly like a raw pointer), which means that it
isn't appropriate for objects with value semantics. And it
doesn't work with most entity objects (which end up with
cycles).

Seriously, there are cases where it is appropriate, but before
you can recommend it, you really have to know the semantics of
his object. (From the name, Widget, I suspect that it isn't
appropriate everywhere, but there are cases where it might be,
e.g. implementing a containment hierarchy.)
 
J

Jonathan Lee

For what use?

For automatic deletion of his objects.
 In this regard, boost::shared_ptr has pointer
semantics (exactly like a raw pointer), which means that it
isn't appropriate for objects with value semantics.

He's already said that raw pointers were fine except that
he didn't like manually deleting the objects.
Seriously, there are cases where it is appropriate, but before
you can recommend it, you really have to know the semantics of
his object.

Well, a) no I don't, and b) he's implied pointer semantics
are fine.

With respect to a), he simply asked for "other approaches",
not "the best approach". Presumably he'll be able to choose
the best from the suggestions. I've given him two.

But maybe you're right and the semantics don't agree. This
time. However, what if in another situation, they do? Should
he come back here and ask again, for his new type of object?
Should he use the method you will recommend for *this*
problem? Wouldn't it be better to have several alternatives
from which he could choose based on his changing needs?

--Jonathan
 
J

James Kanze

For automatic deletion of his objects.

Which begs the question? Why do you want automatic deletion?
In what circumstances?
He's already said that raw pointers were fine except that he
didn't like manually deleting the objects.

Which makes one ask questions. Since pointers (raw or
otherwise) have different semantics than using the objects
themselves. You simply can't replace one with the other.
Well, a) no I don't, and b) he's implied pointer semantics
are fine.
With respect to a), he simply asked for "other approaches",
not "the best approach". Presumably he'll be able to choose
the best from the suggestions. I've given him two.
But maybe you're right and the semantics don't agree.

That wasn't my point. My point was that, basically, he's asking
the wrong question. That the answer is upstream. In the
design. He's suggested that it doesn't matter whether pointer
semantics or value semantics are used. Which suggests that he
hasn't thought things out enough to be able to make the
decision, because they are radically different.
This time. However, what if in another situation, they do?
Should he come back here and ask again, for his new type of
object? Should he use the method you will recommend for
*this* problem? Wouldn't it be better to have several
alternatives from which he could choose based on his changing
needs?

Only once he's understood the more fundamental differences with
regards to the semantics.
 
J

Jonathan Lee

That wasn't my point. My point was that, basically, he's asking
the wrong question.  That the answer is upstream.  In the
design.

I understand your point; I'm just exploring the only part
of your point that is relevant. i.e., if it turns out pointer
semantics are correct, then there's no point in discussing
it. The only interesting line of discussion is if the
semantics don't agree.

My point, in turn, was this: what do you mean by "wrong question"?
He asked a question. How can it be wrong? It might certainly
lead to another question (i.e., which of these alternatives is
best for this situation), but it's a little odd to say his
question is wrong.

In the end, I'm just answering the question that was asked.
You're answering the question you wish he asked. I see them
as complementary questions, one asked before the other. If
you see them as exclusive, then you're essentially saying
he should never be introduced to the information. If that's
too drastic, then you're merely objecting to the order in
which the information has been given to him. To which my
only response can be "whoopty-doo".

--Jonathan
 
J

James Kanze

If you wanted a container of polymorphic objects (i.e.
std::vector<base*>) then shared_ptr is ideal.

That's very debatable. Most polymorphic objects are entity
objects, for which the semantics of shared_ptr are not
appropriate.
The question of whether this is "pointer semantics" or "value
semantics" is moot.

Probably:). (There are a few special cases where you want
polymorphism and value semantics, but they aren't very common.
And of course, they require special handling in C++---something
like the letter envelope idiom.)
 
J

James Kanze

Actually unique_ptr is ideal but as we don't have that yet (C++0x)
tr1/boost::shared_ptr suffices.

Actually, most of the time, neither is appropriate.

On the other hand, IIRC, his original posting named the class
Widget. If it really is a GUI widget, the containers are being
used to implement the containment hierarchy, and containment
implies ownership---usually the case---, then unique_ptr, or
even shared_ptr, might be a good choice. But there are a lot of
if's in there.

Which is really my point. Not only do pointers and values have
significantly different semantics (which the original poster
doesn't seem to be aware of), but shared_ptr has significantly
different semantics than a raw pointer, which also have to be
taken into account.
 
G

Gert-Jan de Vos

Which is really my point.  Not only do pointers and values have
significantly different semantics (which the original poster
doesn't seem to be aware of), but shared_ptr has significantly
different semantics than a raw pointer, which also have to be
taken into account.

What exactly is the problem with shared_ptr? It is often recommended
as the preferred way to deal with dynamically allocated objects. I
understand the potential for cyclic references. For me, weak_ptr
usually fixes that. If a new-ed object has a single owner, I make
it a scoped_ptr. If ownership is shared, I use shared_ptr. What am
I missing?
 
J

Jeff Flinn

James said:
That's very debatable. Most polymorphic objects are entity
objects, for which the semantics of shared_ptr are not
appropriate.

Why not? Can you expound on that statement.

Jeff
 
A

Angus

I assume he means you should not be using something with shared ownership
semantics if it is only owned by one container.  This is why I said
unique_ptr is actually ideal (but isn't yet available so shared_ptr
suffices) or use the Boost pointer containers.  Reinventing the wheel to
ensure objects are deleted is far from ideal.

/Leigh- Hide quoted text -

- Show quoted text -

I am a bit lost with the discussion as to whether the widgets are
value semantics or pointer semantics. The widget is an object with
possibly quite a bit of member data. The term widget was a generic
term I used. But really the object is a contact record. Eg would
have name, address, telephone number.
 
N

Nick Keighley

I understand your point; I'm just exploring the only part
of your point that is relevant. i.e., if it turns out pointer
semantics are correct, then there's no point in discussing
it. The only interesting line of discussion is if the
semantics don't agree.

My point, in turn, was this: what do you mean by "wrong question"?
He asked a question. How can it be wrong?

a question contains presupersitions about the world. Untangling these
assumptions may be more educational than answering the straight
question. I used to know a guy who seemed to answer all questions with
"why do you want ot do that?". Mostly he was right (if nothing else it
made you think hard before you asked the question!).

The OP may (like me) be a little vague about the terms "value
sematics", "pointer semantics" and "entity object". Maybe if he had
these straight then questions like "what is the best way to store
objects" wouldn't get asked. It rather depends on the object.
It might certainly
lead to another question (i.e., which of these alternatives is
best for this situation), but it's a little odd to say his
question is wrong.

"have you stopped beating your wife"
"is yellow?"

In the end, I'm just answering the question that was asked.
You're answering the question you wish he asked.

I think he's asking a meta question
I see them
as complementary questions, one asked before the other.

but which is asked first?
If
you see them as exclusive, then you're essentially saying
he should never be introduced to the information. If that's
too drastic, then you're merely objecting to the order in
which the information has been given to him. To which my
only response can be "whoopty-doo".

it is an odd question... It implies there is one simple, one size fits
all answer which I doubt very much.

"what is the best way to travel somewhere?"
 
J

Jonathan Lee

a question contains presupersitions about the world. Untangling these
assumptions may be more educational than answering the straight
question.

It *may* be. Also, please take note: I'm not objecting to James'
approach. He objected to mine. I think the world is big enough
for both. I've already said I see them as complementary.
The OP may (like me) be a little vague about the terms "value
sematics", "pointer semantics" and "entity object". Maybe if he had
these straight then questions like "what is the best way to store
objects" wouldn't get asked. It rather depends on the object.

Or maybe he knows these things and is asking a C++ question because
he switched languages. In any event, if he's confused about the
options he is perfectly free to ask for clarification.
"have you stopped beating your wife"

A question that (phrased in a certain way) leads to fallacious
reasoning and/or undesirable answers. But questions cannot be
wrong because they aren't truth-evaluable. Think how awkward
this sounds:

"Is it cold outside?"
"Wrong."
"is yellow?"

That's not even a question. It's a sentence fragment with
a question mark. Since it is not a question the adjective
'wrong' fails to even apply.

Surely, however, James means that the question asked is
of little utility. Nonetheless, the question was asked
and it's not unreasonable for me to answer it as is.

--Jonathan
 
J

James Kanze

What exactly is the problem with shared_ptr?

It generally doesn't have the desired semantics.
It is often recommended as the preferred way to deal with
dynamically allocated objects.

Most (not all) of the time, if it has the appropriate semantics,
the object has value semantics, and shouldn't be dynamically
allocated anyway.
I understand the potential for cyclic references.

That's part of it, and causes shared_ptr to fail without a lot
of extra work (as much or more as would be required to analyse
object lifetime correctly). Even more often is the fact that it
doesn't have the desired semantics.
For me, weak_ptr usually fixes that. If a new-ed object has a
single owner, I make it a scoped_ptr. If ownership is shared,
I use shared_ptr. What am I missing?

The fact that ownership for most objects that should be
dynamically allocated belongs to the object. And you can't make
this a shared_ptr (and I don't know that that would mean if you
did). And when the object does belong to another object, that
other object will delete it in response to an external event, so
shared_ptr doesn't have the correct semantics there either.

(There are cases where shared_ptr might be appropriate,
particularly as an internal, implementation detail, But they
certainly don't represent the majority of cases.)
 
J

James Kanze

You are missing nothing, shared_ptr is fine for now, but
unique_ptr is ideal but not yet available. unique_ptr wins
because if your container "owns" the dynamic objects you do
not need sharing semantics (or its overhead). Boost also has
"pointer containers".

All of which is true for one particular use of pointers,
implementing a containment hierarchy. But that's far from the
only, or even the most frequent use of pointers. In most of the
typical cases, shared_ptr simply doesn't work.
 
J

James Kanze

Why not? Can you expound on that statement.

The obvious answer is, because it doesn't have the desired
semantics. What are the semantics of shared_ptr? What are you
using your pointers for?

At least in the applications I've seen, most pointers are for
navigation, and most dynamic objects have deterministic
lifetimes. Boost::shared_ptr does not have the semantics of a
pointer used for navigation, and it prevents proper
deterministic lifetime.
 
J

James Kanze

I assume he means you should not be using something with
shared ownership semantics if it is only owned by one
container.

Actually, I'd argue against that, because the name implies
sharing, and there is no sharing involved. Still, in the
absence of other, better named tools, it will do, provided it is
hidden, as an implementation detail.
This is why I said unique_ptr is actually ideal (but isn't yet
available so shared_ptr suffices) or use the Boost pointer
containers.

For such cases, I agree. But how often does a container "own"
an object. (Formally, I'd argue never. If not, it's not just a
container, but something more. But again, it's often
convenient, from an implementation point of view, to allow it to
do so.)
Reinventing the wheel to ensure objects are deleted is far
from ideal.

There's no necessity of reinventing the wheel, since no special
tool is necessary. Most objects have deterministic lifetimes.
 
J

James Kanze

[...]
I am a bit lost with the discussion as to whether the widgets
are value semantics or pointer semantics. The widget is an
object with possibly quite a bit of member data. The term
widget was a generic term I used. But really the object is a
contact record. Eg would have name, address, telephone
number.

The question is whether it has an identity. Roughly speaking,
is the indentity of the object important when you deal with it,
or would any other instance with the same values do as well. If
copying is fully acceptable, the obvious solution is to copy,
and forget about dynamic allocation. If instances need
identity, and operating on a copy is not acceptable, then of
course, the object shouldn't support copy, and unless (usually
highly unlikely for such objects) its lifetime corresponds
exactly to a scope, you have to allocate it dynamically.

The classical example is a BankAccount and a MonetaryValue. If
you're crediting 100 Euros (MonetaryValue) to my BankAccount, I
don't care how many times the MonetaryValue has been
copied---one instance is as good as another, as long as the
value is 100 Euros. On the other hand, I would be most upset if
the BankAccount credited was an on stack copy of my actual
account, which was destructed when it went out of scope, while
the actual account went unchanged.

Note that for MonetaryValue, you will normally never allocate
any instances dynamically, and for BankAccount, the single
instance for my account will be explicitly deleted when the
account is closed (an external event); it is the role of
deletion to remove any pointers to it, and not the role of
pointers to maybe remove the account some day or other (or
never, depending).
 
A

Angus

    [...]
I am a bit lost with the discussion as to whether the widgets
are value semantics or pointer semantics.  The widget is an
object with possibly quite a bit of member data.  The term
widget was a generic term I used.  But really the object is a
contact record.  Eg would have name, address, telephone
number.

The question is whether it has an identity.  Roughly speaking,
is the indentity of the object important when you deal with it,
or would any other instance with the same values do as well.  If
copying is fully acceptable, the obvious solution is to copy,
and forget about dynamic allocation.  If instances need
identity, and operating on a copy is not acceptable, then of
course, the object shouldn't support copy, and unless (usually
highly unlikely for such objects) its lifetime corresponds
exactly to a scope, you have to allocate it dynamically.

The classical example is a BankAccount and a MonetaryValue.  If
you're crediting 100 Euros (MonetaryValue) to my BankAccount, I
don't care how many times the MonetaryValue has been
copied---one instance is as good as another, as long as the
value is 100 Euros.  On the other hand, I would be most upset if
the BankAccount credited was an on stack copy of my actual
account, which was destructed when it went out of scope, while
the actual account went unchanged.

Note that for MonetaryValue, you will normally never allocate
any instances dynamically, and for BankAccount, the single
instance for my account will be explicitly deleted when the
account is closed (an external event); it is the role of
deletion to remove any pointers to it, and not the role of
pointers to maybe remove the account some day or other (or
never, depending).

The contact record does exist and a class managing the contact record
must keep track of the state of the record, it is not read-only. For
example, if a user changes the contact record a notification of the
change is received. Like the bank account, we do not want to edit a
copy of the contact on the stack. So on this basis contact has
reference semantics? Agree?

In addition, due to the fact that we only want to maintain one copy of
each contact, we should disable copying and access the individual
objects via reference?
 

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,994
Messages
2,570,223
Members
46,814
Latest member
SpicetreeDigital

Latest Threads

Top