Why it's a pointer not just a class

M

Michael

Hi,

typedef std::vector<Vehicle*> VehicleList;

Vehicle is a UDT, or a class. Why is Vehicle* not just Vehicle in < >.
Which one is better and why? If you could explain it by laying out some
codes, it would be highly appreciated!

Thanks in advance,
Michael
 
D

Daniel T.

Michael said:
Hi,

typedef std::vector<Vehicle*> VehicleList;

Vehicle is a UDT, or a class. Why is Vehicle* not just Vehicle in < >.
Which one is better and why? If you could explain it by laying out some
codes, it would be highly appreciated!

class Vehicle { };
class Car : public Vehicle { };

vector<Vehicle> objVec;
objVec.push_back( Car() );
// bad, slices all Car behavior off of the object

vector<Vehicle*> ptrVec;
ptrVec.push_back( new Car() ); // good, all Car behavior is maintained

The problem with the second construct is that the vector doesn't know
when to delete the objects it contains. Some other object must track
that. This is often done with some sort of smart pointer.

vector< shared_ptr< Vehicle > > smartVec;
 
S

Salt_Peter

Michael said:
Hi,

typedef std::vector<Vehicle*> VehicleList;

Vehicle is a UDT, or a class. Why is Vehicle* not just Vehicle in < >.
Which one is better and why? If you could explain it by laying out some
codes, it would be highly appreciated!

Thanks in advance,
Michael

Because a Vehicle is abstract. Your goal here is to create a container
that holds derivatives of the Vehicle class. A Car is a type of
vehicle. A Truck is a type of Vehicle. A Bus is a type of Vehicle. Why
have to create a new container for each new type when a container of
Vehicles can store any type that is related to Vehicles, including a
mix of Vehicles?

Polymorphicly, you are creating a container of pointers to Vehicle
derivatives. That pointer to each element then allows the appropriate
behaviour to happen: brake(), reverse(), accelerate(). For example, a
car will not reverse() like a truck - a truck needs to sound a warning
buzzer first. The pointer essentially lets the vtable do its "magic".

As far as code is concerned:

#include <iostream>
#include <ostream>
#include <vector>

class Vehicle
{
public:
Vehicle() { }
virtual ~Vehicle() { } // virtual
virtual void reverse() = 0; // pure virtual -- see below
};

class Car : public Vehicle
{
public:
Car() { }
~Car() { std::cout << "~Car()" << std::endl; }
void reverse() { std::cout << "reversing" << std::endl; }
};

class Truck : public Vehicle
{
public:
Truck() { }
~Truck() { std::cout << "~Truck()" << std::endl; }
void reverse() { std::cout << "buzzer, reversing" << std::endl; }
};

int main()
{
std::vector< Vehicle* > vehicles;
// vehicles.push_back( new Vehicle ); error - Vehicle is abstract
vehicles.push_back( new Car );
vehicles.push_back( new Truck );

vehicles[ 0 ]->reverse();
vehicles[ 1 ]->reverse();

for ( int i = 0; i < vehicles.size(); ++i ) // or size_t
{
delete vehicles[ i ];
}
return 0;
}

/*
reversing
buzzer, reversing
~Car()
~Truck()
*/

The destructors are virtual too, they have to be for this to work. The
reason that reverse() was declared pure virtual in Vehicle is to force
the client programmer to provide a reverse() member function in order
to use this system.
Note that i can write a SportsCar class which derives from Car and
still use it in the vehicles container. A SportsCar is_a Car which is_a
Vehicle, hence a SportsCar is_a Vehicle.
If you don't yet see the benefits of such a system, consider
encapsulating the std::vector in a container class: all of a sudden,
you'ld have a container that can hold any Vehicle - including those
derivatives that have not been created yet (ie: SuperSportsCar or
TenTonTruck). Without having to modify the container in any way.
Thats a very powerful concept - your code becomes extensible without
having to modify the original classes.
 
H

Heinz Ozwirk

Michael said:
Hi,

typedef std::vector<Vehicle*> VehicleList;

Vehicle is a UDT, or a class. Why is Vehicle* not just Vehicle in < >.

Look how Vehicle is defined and how it is used.
Which one is better and why?

Which one is better - a hammer or a tooth brush - and why?

None is better than the other. It depends on what you want to do.

Heinz
 
R

Roland Pibinger

typedef std::vector<Vehicle*> VehicleList;

Vehicle is a UDT, or a class. Why is Vehicle* not just Vehicle in < >.
Which one is better and why? If you could explain it by laying out some
codes, it would be highly appreciated!

Good question. The answer simply is that the creator of STL hates OO
programming. He designed STL for values (ints, doubles, small structs,
....) only. BTW, if you want it you can have it, a STL-container for
(non-copyable) polymorphic objects:
http://www.codeproject.com/vcpp/stl/ptr_vecto.asp

Best wishes,
Roland Pibinger
 
H

Howard

Roland Pibinger said:
Good question. The answer simply is that the creator of STL hates OO
programming. He designed STL for values (ints, doubles, small structs,
...) only. BTW, if you want it you can have it, a STL-container for
(non-copyable) polymorphic objects:
http://www.codeproject.com/vcpp/stl/ptr_vecto.asp

What does the "creator of STL" have to do with anything?

It's simply a question on the usage of it, and when/why one would prefer to
use pointers instead of objects in a vector.

And the answer, as has been pointed out elsethread, is that using pointers
allows one to take advantage of polymorphism, by storing pointers to base
class objects while actually instanciating a variety of derived-class
instances.

-Howard
 
R

red floyd

Howard said:
What does the "creator of STL" have to do with anything?

It's simply a question on the usage of it, and when/why one would prefer to
use pointers instead of objects in a vector.

And the answer, as has been pointed out elsethread, is that using pointers
allows one to take advantage of polymorphism, by storing pointers to base
class objects while actually instanciating a variety of derived-class
instances.

Roland has made his distaste for pointers in Standard containers very
evident in his other posts.
 
R

Roland Pibinger

It's simply a question on the usage of it, and when/why one would prefer to
use pointers instead of objects in a vector.
And the answer, as has been pointed out elsethread, is that using pointers
allows one to take advantage of polymorphism, by storing pointers to base
class objects while actually instanciating a variety of derived-class
instances.

Please take a look at other container libraries apart from STL (Rogue
Wave, MFC, Qt3 ...). They have containers for pointers and containers
for values. It's just STL that's 'value only'. Rouge Wave e.g. has a
RWTPtrDlist for pointers and a RWTValDlist vor values. The OP's
questions is valid.

Best regards,
Roland Pibinger
 
R

Roland Pibinger

Roland has made his distaste for pointers in Standard containers very
evident in his other posts.

Of course, that's not a matter of taste or distaste. If you want to
understand STL you simply need to understand the consequences of value
semantics.

Best wishes,
Roland Pibinger
 
H

Howard

Roland Pibinger said:
Please take a look at other container libraries apart from STL (Rogue
Wave, MFC, Qt3 ...). They have containers for pointers and containers
for values. It's just STL that's 'value only'. Rouge Wave e.g. has a
RWTPtrDlist for pointers and a RWTValDlist vor values. The OP's
questions is valid.

I haven't said that the OP's questions are not valid. I'm suggesting that
you're not addressing his questions, because you're misunderstanding what
was asked.

He asked why some particular code which used a vector was written the way it
was. Also, he asked when and why one would want to use pointers in a
vector, as in the example. And that was answered, by me and by others.

Your response implied he was asking something about why vector the vector
class itself is written the way it is.

I could be wrong on what he or you meant, but that's how I (and apparently
the others) interpreted his questions (and your response).

-Howard
 
R

Roland Pibinger

I haven't said that the OP's questions are not valid. I'm suggesting that
you're not addressing his questions, because you're misunderstanding what
was asked.
He asked why some particular code which used a vector was written the way it
was. Also, he asked when and why one would want to use pointers in a
vector, as in the example. And that was answered, by me and by others.
Your response implied he was asking something about why vector the vector
class itself is written the way it is.
I could be wrong on what he or you meant, but that's how I (and apparently
the others) interpreted his questions (and your response).

Maybe I've iterpreted too much into the question. IMO, it's worth to
point out that there exist C++ libraries other than STL where a
container of pointes to Vehicles would be written as e.g.:

int main(){
RWTPtrDlist<Vehicle> vehicles;
//...
}
see:
http://www.roguewave.com/support/docs/sourcepro/edition9/html/toolsref/rwtptrdlist.html


Best regards,
Roland Pibinger
 
K

Kai-Uwe Bux

Roland said:
Of course, that's not a matter of taste or distaste. If you want to
understand STL you simply need to understand the consequences of value
semantics.

Well, that goes for the language as a whole. Class type variables in C++
have value semantics with all its (surprising) consequences like slicing.
The design decision for the containers in STL is firmly rooted in the
fundamental feature of C++ to have value semantics and fake/model reference
semantics through pointers. I do not think that this trait of C++ needs to
be understood for STL containers separately.


Best

Kai-Uwe Bux
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

Roland said:
Maybe I've iterpreted too much into the question. IMO, it's worth to
point out that there exist C++ libraries other than STL where a
container of pointes to Vehicles would be written as e.g.:

int main(){
RWTPtrDlist<Vehicle> vehicles;
//...
}

There are also libraries with threads, windows, directories, symbolic
algebra... I don't see the need to point the things the standard library
does not have. It's shorter the other way.
 
A

Angelo Fraietta

I believe another important things is that storing pointers would not
result in default constructors being called or required. Also, using a
pointer would not call a copy constructor when placing the object into
the container.
 
B

Bo Persson

Angelo Fraietta said:
I believe another important things is that storing pointers would
not result in default constructors being called or required.

Default constructors are only required if you actually use them, like
in resize().
Also, using a pointer would not call a copy constructor when placing
the object into the container.

True. You shouldn't store objects that are expensive to copy in a
standard container.

On the other hand, the destructor for the elements is not called when
an element is removed, or when the vectors is destructed. That might
add additional code and book keeping elsewhere in the program.


Bo Persson
 
R

Roland Pibinger

You shouldn't store objects that are expensive to copy in a
standard container.

It's not mainly a question whether the container elements are
expensive to copy. The important distinction is between 'values' and
'objects' (objects in the OO sense). Objects are usually non-copyable
but may be accessed through multiple references (pointers in C++). STL
suppports only values. That causes the problems.

Best wishes,
Roland Pibinger
 
E

Earl Purple

Roland said:
Please take a look at other container libraries apart from STL (Rogue
Wave, MFC, Qt3 ...). They have containers for pointers and containers
for values. It's just STL that's 'value only'. Rouge Wave e.g. has a
RWTPtrDlist for pointers and a RWTValDlist vor values. The OP's
questions is valid.

and then there is, of course, boost:

http://www.boost.org/libs/ptr_container/doc/ptr_container.html

I'm not sure if this is the same one that was in TheSourceProject or a
different one. I haven't taken time to look. The boost implementation
uses "thin templates" i.e. they do the implementation with void*
pointers and cast both ways. Not sure if that is always safe in a
mutliple-inheritance situation, but it can have its advantages, i.e.
far less code generation and even allows you to compile some of it into
object code to be linked in.
 
I

I V

and then there is, of course, boost:
[snip]

Not to mention boost::indirect_iterator,
http://boost.org/libs/iterator/doc/indirect_iterator.html

I'm not familiar with the Rogue Wave library, but a quick glance at the
documentation suggests that combining the standard containers with
boost::indirect_iterator accomplishes most, if not all, of what the RW
pointer containers do (from what I can tell, the RW classes don't deal
with ownership issues, which is probably the hardest part of a pointer
container to figure out).
 
R

Roland Pibinger

:indirect_iterator,
http://boost.org/libs/iterator/doc/indirect_iterator.html

I'm not familiar with the Rogue Wave library, but a quick glance at the
documentation suggests that combining the standard containers with
boost::indirect_iterator accomplishes most, if not all, of what the RW
pointer containers do

Nope. When you use eg. std::sort with an boost::indirect_iterator it
swaps pointed-to objects not pointers. Containers, iterators and
algorithms need to become pointer aware for a real solution. Moreover,
the STL templates are notorious for their compiler error messages. An
iterator adapter adds another level of obfuscation to the program.
(from what I can tell, the RW classes don't deal
with ownership issues, which is probably the hardest part of a pointer
container to figure out).

Why should a container for pointers be usable only for objects
allocated with new? Why is such a container supposed to delete
contained objects? A basic library container should not intermingle
container functionality and ownership. It should be usable for all
pointers (to heap-allocatd, to stack and to global objects). IMO, it's
a design flaw to write containers that insert pointers and require
that these pointers _must_ point to heap-allocated objects (which are
later deleted by the container). Of course, one can implement
ownership semantics on top of a basic pointer container but to build
ownership (and transfer of ownership to the container) into the
container is (put mildly) a highly questionable design decision.

Best wishes,
Roland Pibinger
 
E

Earl Purple

Roland said:
Why should a container for pointers be usable only for objects
allocated with new? Why is such a container supposed to delete
contained objects?

It shouldn't have to. But there are containers for those that don't.
When I saw boost's ptr_vector my first impression was that perhaps
there should have been a custom deleter option, although the
custom-deleter would need to be the same for every object in the
vector.

If you want custom deleters for each pointer then use vector<
shared_ptr >.
And for sort you have the option of passing ni your own predicate that
can do *iter1 < *iter2
A basic library container should not intermingle
container functionality and ownership. It should be usable for all
pointers (to heap-allocatd, to stack and to global objects). IMO, it's
a design flaw to write containers that insert pointers and require
that these pointers _must_ point to heap-allocated objects (which are
later deleted by the container).

It's not a design flaw it's a design decision. You use that collection
when you want that functionality. When you don't, you use a different
one. It enables you to use std::generate to fill the collection where
std::generate calls new.
Of course, one can implement
ownership semantics on top of a basic pointer container but to build
ownership (and transfer of ownership to the container) into the
container is (put mildly) a highly questionable design decision.

In the absence of real garbage collection, you must provide ownership
semantics. And assuming you don't want to use shared_ptr, using a
collection to handle the it for you is not unreasonable.

I do think though that there should be an option of a custom deleter,
albeit one deleter for every item in the collection so there is no
option to mix and match. Not unless the deleter is smart enough to do
that, that is.
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top