class Parent
{
virtual void doStuff();
}
class Child : public Parent
{
virtual void doStuff();
}
and this function
bool foo(Parent *p)
{
if(!p)
p = new Parent();
p->doStuff();
}
I would like foo() to take a pointer and if it is null allocate the
memory for the class I pass in.
For example
Child * c = NULL;
foo(c);
would result in a Child() constructor and Child::doStuff() being
called.
The way it is now Parent() and Parent::doStuff() will get called.
How can this be done while keeping foo defined as foo(Parent *p) ????
I've thought of using templates like this:
template<class T>
bool foo(Parent* p)
{
if(!p) p = new T();
p->doStuff();
}
but I am unable to get the correct type from a second templated class
that has been passed T = Child* as its template type.
As a follow up can I get a template value T = Child* and somehow pass
foo T=Child ???
Pay attention, you will learn something today.
You said you don't want to change the function's signature so this
would work dandy except for a few problems, i'll try and point out
those to you below:
template<class T>
void foo(Parent* p)
{
if(!p) p = new T();
p->doStuff();
}
and you call it like so:
foo< Child >(pc);
or
foo< Parent >(pc);
___
Now, the important parts. In this language its bad news to distribute
allocation and deallocation, the above code is the perfect example why
that rule is so important. When you pass pointers like so:
Child* pc = 0;
foo< Child >(pc);
the pointer pc never gets modified in main, only its copy in foo does,
so once foo returns you've got a memory leak. And to compound the
issue, foo's Parent* p is no more and we can no longer release your
allocated Child. So if you were to:
if(!pc)
delete pc;
you are in fact deleting nothing. Hence:
template<class T>
void foo(Parent* p)
{
if(!p) p = new T();
p->doStuff();
delete p; // required
}
Which then brings up another issue. virtual destructors.
Whenever you store derived allocations using a pointer to base, you
must declare you base d~tor virtual or you'll end up only deallocating
a portion of your objects.
class Parent
{
public:
virtual ~Parent()
{
std::cout << "~Parent()\n";
}
virtual void doStuff()
{
std::cout << "Parent::doStuff()\n";
}
};
test it, try the d~tor without 'virtual' and delete Parent* p = new
Child.
To solve the original problem involving distributed allocations, you
can pass a pointer by reference, use a smart pointer or some form of
factory that manages allocations and deallocations for you.
If Child is copyable and assigneable, std::vector< Child > or
std::deque< Child > would be the perfect solution. A factory composed
of std::vector< Parent* > would do nicely too.
> bool foo(Parent *p)
> {
> if(!p)
> p = new Parent();
> p->doStuff();
>
> }
>
> I would like foo() to take a pointer and if it is null allocate the
> memory for the class I pass in.
Can it be done at a higher level? Because such side effects make the
code more complicated.
>
> For example
>
> Child * c = NULL;
> foo(c);
>
> would result in a Child() constructor and Child::doStuff() being
> called.
> The way it is now Parent() and Parent::doStuff() will get called.
>
> How can this be done while keeping foo defined as foo(Parent *p) ????
I may be missing something, but isn't this what you want?
bool foo(Parent *p)
{
if(!p) {
p = new Child();
}
p->doStuff();
}
Of course 'p' is being leaked there as was in your original
function. :)
Ali
should really be:
template< typename T >
> void foo(Parent* p)
> {
> if(!p) p = new T();
> p->doStuff();
>
> }
>
> and you call it like so:
>
> foo< Child >(pc);
> or
> foo< Parent >(pc);
>
> ___
> Now, the important parts. In this language its bad news to distribute
> allocation and deallocation, the above code is the perfect example why
> that rule is so important. When you pass pointers like so:
>
> Child* pc = 0;
> foo< Child >(pc);
>
> the pointer pc never gets modified in main, only its copy in foo does,
> so once foo returns you've got a memory leak. And to compound the
> issue, foo's Parent* p is no more and we can no longer release your
> allocated Child. So if you were to:
>
> if(!pc)
> delete pc;
>
> you are in fact deleting nothing. Hence:
>
> template<class T>
template< typename T >
> void foo(Parent* p)
> {
> if(!p) p = new T();
> p->doStuff();
> delete p; // required
>
> }
>
> Which then brings up another issue. virtual destructors.
> Whenever you store derived allocations using a pointer to base, you
> must declare you base d~tor virtual or you'll end up only deallocating
> a portion of your objects.
>
> class Parent
> {
> public:
> virtual ~Parent()
> {
> std::cout << "~Parent()\n";
> }
> virtual void doStuff()
> {
> std::cout << "Parent::doStuff()\n";
> }
>
> };
>
> test it, try the d~tor without 'virtual' and delete Parent* p = new
> Child.
>
> To solve the original problem involving distributed allocations, you
> can pass a pointer by reference, use a smart pointer or some form of
> factory that manages allocations and deallocations for you.
> If Child is copyable and assigneable, std::vector< Child > or
> std::deque< Child > would be the perfect solution. A factory composed
> of std::vector< Parent* > would do nicely too.- Hide quoted text -
>
> - Show quoted text -
>>
>> template<class T>
>
> should really be:
> template< typename T >
I guess I'll have to go through the C++ standard and change every
template declaration to use "typename" instead of "class".
This is a style thing. Some people prefer "typename" for unfathomable
reasons, and right-thinking programmers use "class".
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)
There is no difference between typename and class - the standard
states something like 'no semantic difference'. Not sure this is the
appropriate place or time for that discussion, anywhooo.
The comment was obviously one based on style. Note that not all
template parameters are actual types.
Stroustrup says somewhere:
template<class T> void foo(T& v)
{
typename T::iterator i = v.begin();
}
disambiguates statements, etc.
I agree with you. "class" is preferable for pragmatic reasons: easier
to type, occupies less real estate in the source files and means the
very same thing in this context.
--
Max
> > and this function
> > For example
Which is, in some ways, a contradiction in terms. He wants the
function to depend on the type passed in, without passing in the
type. The obvious solution for the function to depend on the
type is to pass in the type, e.g.:
template< typename T >
bool foo( T* p ) ...
Of course, this doesn't solve the general problem: what to do if
he wants to call the function with "foo( NULL )".
> so this would work dandy except for a few problems, i'll try
> and point out those to you below:
> template<class T>
> void foo(Parent* p)
> {
> if(!p) p = new T();
> p->doStuff();
> }
> and you call it like so:
> foo< Child >(pc);
> or
> foo< Parent >(pc);
> Now, the important parts. In this language its bad news to
> distribute allocation and deallocation,
Which is simply false. The rule is almost the opposite: if you
don't distribute allocation and deallocation, you shouldn't be
using dynamic allocation to begin with. The most important
single reason for using dynamic allocation is because you need
explicit deallocation, elsewhere in the program.
His case is fairly special (so special that I've never seen it
in 20 years of C++). (But I suspect that he's not described his
problem in enough detail.)
> the above code is the perfect example why that rule is so
> important. When you pass pointers like so:
> Child* pc = 0;
> foo< Child >(pc);
> the pointer pc never gets modified in main, only its copy in
> foo does, so once foo returns you've got a memory leak.
> And to compound the issue, foo's Parent* p is no more and we
> can no longer release your allocated Child. So if you were to:
> if(!pc)
> delete pc;
> you are in fact deleting nothing. Hence:
> template<class T>
> void foo(Parent* p)
> {
> if(!p) p = new T();
> p->doStuff();
> delete p; // required
> }
Which will wreck havoc if he calls the function with a pointer
allocated elsewhere (or pointing to a local object). What he
needs is some sort of manager class:
template< typename T >
class PtrManager
{
public:
PtrManager( Parent* p )
: myPtr( p == NULL ? new T : p )
, myIsOwned( p == NULL )
{
}
~PtrManager()
{
if ( myIsOwned ) {
delete p ;
}
}
Parent* operator->() const
{
return myPtr ;
}
private:
Parent* myPtr ;
bool myIsOwned ;
} ;
This will also save him if p->doStuff() throws.
> Which then brings up another issue. virtual destructors.
> Whenever you store derived allocations using a pointer to
> base, you must declare you base d~tor virtual or you'll end up
> only deallocating a portion of your objects.
No, you'll end up with undefined behavior, which is worse. It
may work, it may seem to work, but leak memory, it may crash
immediately, it may corrupt the free space arena, causing a
crash in some totally unrelated code, or it may do just about
anything else.
> class Parent
> {
> public:
> virtual ~Parent()
> {
> std::cout << "~Parent()\n";
> }
> virtual void doStuff()
> {
> std::cout << "Parent::doStuff()\n";
> }
> };
> test it, try the d~tor without 'virtual' and delete Parent* p
> = new Child.
> To solve the original problem [...]
We have to know what the original problem really was:-). (I
wonder, for example, if he didn't think that his allocation
actually did modify the original pointer.)
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> > On 2008-10-30 18:00:18 -0400, Salt_Peter <pj_h...@yahoo.com> said:
> > >> template<class T>
> > > should really be:
> > > template< typename T >
> > I guess I'll have to go through the C++ standard and change
> > every template declaration to use "typename" instead of
> > "class".
> > This is a style thing. Some people prefer "typename" for
> > unfathomable reasons, and right-thinking programmers use
> > "class".
> I agree with you. "class" is preferable for pragmatic reasons:
> easier to type,
Funny, I don't find either easier to type than the other.
> occupies less real estate in the source files
Which means?
> and means the very same thing in this context.
To the compiler. To the human reader, perhaps not.
I use typename here, because it says what I mean. Literally,
both to the human reader and to the compiler.
And then there's the camp that uses 'class' when a template
requires a class and 'typename' otherwise.
Schobi
(yes, I'm a member of that one)