By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,797 Members | 1,191 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,797 IT Pros & Developers. It's quick & easy.

Virtual Methods Question

P: n/a
I have the following three classes

class A
{
public:
virtual void f() = 0;
};

class B: public A
{
public:
virtual void f();
};

void B::f()
{
cout << "B::f()";
}

class C: public B
{
public:
void f();
};

void C::f()
{
cout << "C::f()";
}
main()
{
A* x = new C();

x->f();
}

The above program prints the following:

B::f()

But, I wanted the program to print

C::f()

Why is the program doing this? Is there any way for me to have C's f()
method invoked instead of B's f() method? I am using the GNU compiler
for VxWorks 5.5. Thanks in advance for any help.
-Daniel

Feb 27 '06 #1
Share this Question
Share on Google+
20 Replies


P: n/a

Daniel wrote:
The above program prints the following:

B::f()

But, I wanted the program to print

C::f()


prints "C::f()" over here. That is what it should do.

Feb 27 '06 #2

P: n/a

"Daniel" <da***********@gmail.com> schrieb im Newsbeitrag
news:11**********************@v46g2000cwv.googlegr oups.com...
class C: public B
{
public:
void f();
};


you miss a virtual there.

f~
Feb 27 '06 #3

P: n/a

"Frank Schmidt" <fs@example.com> wrote in message
news:dt**********@online.de...
|
| "Daniel" <da***********@gmail.com> schrieb im Newsbeitrag
| news:11**********************@v46g2000cwv.googlegr oups.com...
|
| > class C: public B
| > {
| > public:
| > void f();
| > };
|
| you miss a virtual there.
|
| f~
|

Nope, if the pure-abstract or base class specifies that void f() is virtual
then its virtual in the entire hierarchy. Regardless whether the derived
classes have that function as virtual or not.

Feb 27 '06 #4

P: n/a

Peter_Julian wrote:
"Frank Schmidt" <fs@example.com> wrote in message
news:dt**********@online.de...
|
| "Daniel" <da***********@gmail.com> schrieb im Newsbeitrag
| news:11**********************@v46g2000cwv.googlegr oups.com...
|
| > class C: public B
| > {
| > public:
| > void f();
| > };
|
| you miss a virtual there.
|
| f~
|

Nope, if the pure-abstract or base class specifies that void f() is virtual
then its virtual in the entire hierarchy. Regardless whether the derived
classes have that function as virtual or not.


I thought it stopped at C meaning that any subclass of C would not be
able to polymorphically override f().

Feb 27 '06 #5

P: n/a

"Daniel" <da***********@gmail.com> wrote in message
news:11**********************@v46g2000cwv.googlegr oups.com...
| I have the following three classes
|
| class A
| {
| public:
| virtual void f() = 0;
| };

undefined behaviour.
1) you failed to invoke delete x
2) In main() you are using a base pointer to a derived object and yet you
have not declared a virtual destructor in class A.

#include <iostream>
#include <ostream>

class A
{
public:
virtual ~A() { std::cout << "~A()\n"; }
virtual void f() = 0;
};

<snip>

|
| main()

int main()

| {
| A* x = new C();

A* x = new C;

|
| x->f();

delete x;

| }
|
| The above program prints the following:
|
| B::f()
|
| But, I wanted the program to print
|
| C::f()
|
| Why is the program doing this? Is there any way for me to have C's f()
| method invoked instead of B's f() method? I am using the GNU compiler
| for VxWorks 5.5. Thanks in advance for any help.
|

Your compiler is invoking the C() ctor correctly(which requires that both
A() and B() get allocated first) and then drops the allocation of the
derived C as if it were a temporary (which is not appropriate but otherwise
not strictly enforced). The remnant is a valid B-type object.

That should be fixed with the correct statement:
A* x = new C; // correctly invokes the default ctor to completion

Note that your problem, other than not using delete x at all (shame), has a
much more critical issue than the one you raise. If you don't provide a
virtual d~tor in class A then ~B() or ~C() will never be invoked. Thats a
guarenteed memory leak.

Test it:

int main()
{
A* x = new C;

x->f();

delete x;

return 0;
}

/* correct output
C::f()
~C()
~B()
~A()
*/

but what you are getting with the compiler generated d~tor(s) is:

C::f()
~A()

which is a partial destruction and a memory leak. bad news.

Moral of the story:

a) never, ever new if you don't delete
b) never, ever new[] if you don't [] delete
c)if you derive and destroy via a pointer to base:
***always provide a virtual d~tor***
or suffer the consequences.

Feb 27 '06 #6

P: n/a

<ro**********@gmail.com> wrote in message
news:11**********************@z34g2000cwc.googlegr oups.com...
|
| Peter_Julian wrote:
| > "Frank Schmidt" <fs@example.com> wrote in message
| > news:dt**********@online.de...
| > |
| > | "Daniel" <da***********@gmail.com> schrieb im Newsbeitrag
| > | news:11**********************@v46g2000cwv.googlegr oups.com...
| > |
| > | > class C: public B
| > | > {
| > | > public:
| > | > void f();
| > | > };
| > |
| > | you miss a virtual there.
| > |
| > | f~
| > |
| >
| > Nope, if the pure-abstract or base class specifies that void f() is
virtual
| > then its virtual in the entire hierarchy. Regardless whether the derived
| > classes have that function as virtual or not.
|
| I thought it stopped at C meaning that any subclass of C would not be
| able to polymorphically override f().
|

That doesn't make sense, and with good reason. The is_a relationship is
critical here. If you publicly derive from C and declare D, then its implied
that the D-type is_a C-type as well. You can perfectly well derive like
so...

class D : public C
{
};

and never declare void f() since a call like so...

A* p_a = new D;
p_a->f(); // will call C::f(), no problem - its expected
delete p_a; // invokes 4 d~tors - ~D, ~C, ~B and ~A

If there was a way to prevent the inheritance of a pure-virtual member
function, polymorphism would be broken. And there is...
___
The answer to your quest is simple: composition or private inheritance.
Sometimes its more important to understand when not_to_use polymorphic
inheritance.
Neither of these represent an "is_a" inheritance scheme.

class D
{
C c;
public:
void f() { } // not virtual
};

or D in_terms_of C, where D is_not C or B or A. This is also a form of
composition, not polymorphic inheritance...

class D : private C
{
void f() { std::cout << "D::f()\n"; } // not virtual
};

int main()
{
D d;
d.f();
}

/*
D::f() // not virtual
~D()
~C()
~B()
~A()
*/

Feb 27 '06 #7

P: n/a
Daniel wrote:
The above program prints the following:

B::f()


You must have made a copying error: the program does not compile
for me. There are missing include statements, namespace qualification,
and a missing return type of 'main()'. Once I fixed these problems,
the program prints "C::f()", as it should.
--
<mailto:di***********@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence
Feb 27 '06 #8

P: n/a

"Peter_Julian" <pj@antispam.codigo.ca> schrieb im Newsbeitrag
news:fN*******************@news20.bellglobal.com.. .

"Frank Schmidt" <fs@example.com> wrote in message
news:dt**********@online.de...
|
| "Daniel" <da***********@gmail.com> schrieb im Newsbeitrag
| news:11**********************@v46g2000cwv.googlegr oups.com...
|
| > class C: public B
| > {
| > public:
| > void f();
| > };
|
| you miss a virtual there.
|
| f~
|

Nope, if the pure-abstract or base class specifies that void f() is
virtual
then its virtual in the entire hierarchy. Regardless whether the derived
classes have that function as virtual or not.


say that to a compiler, which calls B::f() with the given the source
Feb 27 '06 #9

P: n/a

Peter_Julian wrote:
<ro**********@gmail.com> wrote in message
news:11**********************@z34g2000cwc.googlegr oups.com...
|
| Peter_Julian wrote:
| > "Frank Schmidt" <fs@example.com> wrote in message
| > news:dt**********@online.de...
| > |
| > | "Daniel" <da***********@gmail.com> schrieb im Newsbeitrag
| > | news:11**********************@v46g2000cwv.googlegr oups.com...
| > |
| > | > class C: public B
| > | > {
| > | > public:
| > | > void f();
| > | > };
| > |
| > | you miss a virtual there.
| > |
| > | f~
| > |
| >
| > Nope, if the pure-abstract or base class specifies that void f() is
virtual
| > then its virtual in the entire hierarchy. Regardless whether the derived
| > classes have that function as virtual or not.
|
| I thought it stopped at C meaning that any subclass of C would not be
| able to polymorphically override f().
|

That doesn't make sense, and with good reason. The is_a relationship is
critical here. If you publicly derive from C and declare D, then its implied
that the D-type is_a C-type as well. You can perfectly well derive like
so...

class D : public C
{
};

and never declare void f() since a call like so...


You totally missed my point. Doesn't matter because I was wrong but
still, I said one thing and you went off in a totally different
direction.

Feb 27 '06 #10

P: n/a

"Frank Schmidt" <fs@example.com> wrote in message
news:dt**********@online.de...
|
| "Peter_Julian" <pj@antispam.codigo.ca> schrieb im Newsbeitrag
| news:fN*******************@news20.bellglobal.com.. .
| >
| > "Frank Schmidt" <fs@example.com> wrote in message
| > news:dt**********@online.de...
| > |
| > | "Daniel" <da***********@gmail.com> schrieb im Newsbeitrag
| > | news:11**********************@v46g2000cwv.googlegr oups.com...
| > |
| > | > class C: public B
| > | > {
| > | > public:
| > | > void f();
| > | > };
| > |
| > | you miss a virtual there.
| > |
| > | f~
| > |
| >
| > Nope, if the pure-abstract or base class specifies that void f() is
| > virtual
| > then its virtual in the entire hierarchy. Regardless whether the derived
| > classes have that function as virtual or not.
|
| say that to a compiler, which calls B::f() with the given the source
|

Wrong solution. The only reason that B::f() is being called is because the
call to new C() never return a type C instance [undefined behaviour]. The
remnant type at the pointer is a B-type object with some compilers.

The correct statement is: A* p = new C; // no brackets

insofar as the virtual keyword, test it yourself...:

___
#include <iostream>
#include <ostream>

class A
{
virtual void f();
};

class B : public A
{
void f();
};

class C : public B
{
void f();
};

int main()
{
C c;
A* p = &c;
p->f();

return 0;
}

/*
C::f()
*/

Feb 28 '06 #11

P: n/a
On Sun, 26 Feb 2006 17:34:58 -0800, Daniel wrote:
I have the following three classes
<snip>

The above program prints the following:

B::f()

But, I wanted the program to print

C::f()

Why is the program doing this? Is there any way for me to have C's f()
method invoked instead of B's f() method?


The way the code is set up above, it should work. Make sure the function
signatures are *identical* including parameters, return type, and
"const"ness.

- jay
Feb 28 '06 #12

P: n/a
On Sun, 26 Feb 2006 23:50:54 -0500, Peter_Julian wrote:

I'll probably regret this...

"Daniel" <da***********@gmail.com> wrote in message
news:11**********************@v46g2000cwv.googlegr oups.com...
| I have the following three classes
|
| class A
| {
| public:
| virtual void f() = 0;
| };

undefined behaviour.
1) you failed to invoke delete x
Failing to delete x is not undefined behavior. It's a memory leak.
2) In main() you are using a base pointer to a derived object and yet you
have not declared a virtual destructor in class A.
*Using* a derived object through a base pointer (without a virtual
destructor) is not undefined behavior (see, for example, COM). *Deleting*
said pointer would be. Since the pointer was not deleted, there is no
undefined behavior. (But as soon as you add your delete, then you need to
add the virtual destructor.) I'm not saying it's good code (and you
provide an excellent analysis below), but there's no undefined behavior as
is.


| {
| A* x = new C();

A* x = new C;

There is no difference in the above two lines for the C class, at least as
far as the stability of the resulting object goes.

Your compiler is invoking the C() ctor correctly(which requires that both
A() and B() get allocated first) and then drops the allocation of the
derived C as if it were a temporary (which is not appropriate but otherwise
not strictly enforced). The remnant is a valid B-type object.


Have you been smoking the C++ standard again? ;) Seriously, what are you
talking about? What in the world would cause the newly allocated "C"
object to decay to a "B"?

- Jay

Feb 28 '06 #13

P: n/a

"Jay_Nabonne" <jn******@pacbello.net> wrote in message
news:pa****************************@pacbello.net.. .
| On Sun, 26 Feb 2006 17:34:58 -0800, Daniel wrote:
|
| > I have the following three classes
|
| <snip>
|
| >
| > The above program prints the following:
| >
| > B::f()
| >
| > But, I wanted the program to print
| >
| > C::f()
| >
| > Why is the program doing this? Is there any way for me to have C's f()
| > method invoked instead of B's f() method?
|
| The way the code is set up above, it should work. Make sure the function
| signatures are *identical* including parameters, return type, and
| "const"ness.
|

The return type is not part of the signature and is not taken in
consideration when deciding which function gets called.

Mar 1 '06 #14

P: n/a

"Jay_Nabonne" <jn******@pacbello.net> wrote in message
news:pa****************************@pacbello.net.. .
| On Sun, 26 Feb 2006 23:50:54 -0500, Peter_Julian wrote:
|
| I'll probably regret this...

lol, no we won't, i welcome the feedback specially if it proves me wrong

|
| >
| > "Daniel" <da***********@gmail.com> wrote in message
| > news:11**********************@v46g2000cwv.googlegr oups.com...
| > | I have the following three classes
| > |
| > | class A
| > | {
| > | public:
| > | virtual void f() = 0;
| > | };
| >
| > undefined behaviour.
| > 1) you failed to invoke delete x
|
| Failing to delete x is not undefined behavior. It's a memory leak.

The mem leak wasn't what made the code UB.

|
| > 2) In main() you are using a base pointer to a derived object and yet
you
| > have not declared a virtual destructor in class A.
|
| *Using* a derived object through a base pointer (without a virtual
| destructor) is not undefined behavior (see, for example, COM). *Deleting*
| said pointer would be. Since the pointer was not deleted, there is no
| undefined behavior. (But as soon as you add your delete, then you need to
| add the virtual destructor.) I'm not saying it's good code (and you
| provide an excellent analysis below), but there's no undefined behavior as
| is.

Wrong, new used to allocate that base pointer, whether its explicitly
deleted or not, is UB. Consider: shared_ptr<T>, scoped_ptr<T>, etc.

|
| >
| >
| > | {
| > | A* x = new C();
| >
| > A* x = new C;
| >
|
| There is no difference in the above two lines for the C class, at least as
| far as the stability of the resulting object goes.

This, unfortunately, has been proven to be false. The OP's result is a case
in point.

|
| >
| > Your compiler is invoking the C() ctor correctly(which requires that
both
| > A() and B() get allocated first) and then drops the allocation of the
| > derived C as if it were a temporary (which is not appropriate but
otherwise
| > not strictly enforced). The remnant is a valid B-type object.
|
| Have you been smoking the C++ standard again? ;) Seriously, what are you
| talking about? What in the world would cause the newly allocated "C"
| object to decay to a "B"?
|

Beats the hell out of me (placement new implementation?). Won't be the first
time i've seen that. It is, however, correct to write ...

int* p = new int;

.... to invoke the default ctor. Even the problem compilers handle that
correctly. I'ld be interested to know what that GNU compiler for VxWorks 5.5
does without the brackets (hint?).

[Hmm, now where the hell is that pint of beer()?]

Mar 1 '06 #15

P: n/a
On Tue, 28 Feb 2006 19:56:44 -0500, Peter_Julian wrote:

"Jay_Nabonne" <jn******@pacbello.net> wrote in message
news:pa****************************@pacbello.net.. .
| On Sun, 26 Feb 2006 17:34:58 -0800, Daniel wrote:
|
| > I have the following three classes
|
| <snip>
|
| >
| > The above program prints the following:
| >
| > B::f()
| >
| > But, I wanted the program to print
| >
| > C::f()
| >
| > Why is the program doing this? Is there any way for me to have C's f()
| > method invoked instead of B's f() method?
|
| The way the code is set up above, it should work. Make sure the function
| signatures are *identical* including parameters, return type, and
| "const"ness.
|

The return type is not part of the signature and is not taken in
consideration when deciding which function gets called.


In general, you are right, but I'm not sure how that relates to this
problem.

Since the function call is through the A*, there is only one
function signature to examine (the one in A). As it turns out, if the
virtual function in C had a different return type, it wouldn't compile
anyway, so my suggestion to check the return type doesn't help here. But
the rest are valid. For example, this code prints "B" because the function
"f" in class C doesn't match the signature of the base class:

class A
{
public:
virtual void f() = 0;
};

class B : public A
{
virtual void f()
{
printf("B\n");
}
};

class C : public B
{
void f() const
{
printf("C\n");
}
};

int main()
{
A* p = new C;
p->f();
return 0;
}

- Jay

Mar 1 '06 #16

P: n/a
On Tue, 28 Feb 2006 22:45:18 -0500, Peter_Julian wrote:

"Jay_Nabonne" <jn******@pacbello.net> wrote in message
news:pa****************************@pacbello.net.. .
| On Sun, 26 Feb 2006 23:50:54 -0500, Peter_Julian wrote:
|
| I'll probably regret this...

lol, no we won't, i welcome the feedback specially if it proves me wrong

|
| >
| > "Daniel" <da***********@gmail.com> wrote in message
| > news:11**********************@v46g2000cwv.googlegr oups.com...
| > | I have the following three classes
| > |
| > | class A
| > | {
| > | public:
| > | virtual void f() = 0;
| > | };
| >
| > undefined behaviour.
| > 1) you failed to invoke delete x
|
| Failing to delete x is not undefined behavior. It's a memory leak.

The mem leak wasn't what made the code UB.

|
| > 2) In main() you are using a base pointer to a derived object and yet
you
| > have not declared a virtual destructor in class A.
|
| *Using* a derived object through a base pointer (without a virtual
| destructor) is not undefined behavior (see, for example, COM). *Deleting*
| said pointer would be. Since the pointer was not deleted, there is no
| undefined behavior. (But as soon as you add your delete, then you need to
| add the virtual destructor.) I'm not saying it's good code (and you
| provide an excellent analysis below), but there's no undefined behavior as
| is.

Wrong, new used to allocate that base pointer, whether its explicitly
deleted or not, is UB. Consider: shared_ptr<T>, scoped_ptr<T>, etc.
Can you provide a reference for that statement? Since no shared_ptr or
scoped_ptr are used here, they're irrelevant. How is allocating the
derived object and not freeing it UB?


| > | {
| > | A* x = new C();
| >
| > A* x = new C;
| >
| >
| There is no difference in the above two lines for the C class, at
| least as far as the stability of the resulting object goes.

This, unfortunately, has been proven to be false. The OP's result is a
case in point.
The following code works fine for me (prints "C"):

class A
{
public:
virtual void f() = 0;
};

class B : public A
{
virtual void f()
{
printf("B\n");
}
};

class C : public B
{
void f() const
{
printf("C\n");
}
};

int main()
{
A* p = new C();
p->f();
return 0;
}

which implies either the OP was using different code or the compiler is
broken.

| > Your compiler is invoking the C() ctor correctly(which requires that
both
| > A() and B() get allocated first) and then drops the allocation of
| > the derived C as if it were a temporary (which is not appropriate
| > but
otherwise
| > not strictly enforced). The remnant is a valid B-type object.
|
| Have you been smoking the C++ standard again? ;) Seriously, what are
| you talking about? What in the world would cause the newly allocated
| "C" object to decay to a "B"?
|
|
Beats the hell out of me (placement new implementation?). Won't be the
first time i've seen that. It is, however, correct to write ...

int* p = new int;

... to invoke the default ctor. Even the problem compilers handle that
correctly. I'ld be interested to know what that GNU compiler for VxWorks
5.5 does without the brackets (hint?).


If you're suggesting it's a compiler error, then I can get in line with
it. I thought you were suggesting that:

A* p = new C();

could (with a correct implementation) be different than:

A* p = new C;

which is not a true statement, to my knowledge and experience.

- Jay

Mar 1 '06 #17

P: n/a

"Jay_Nabonne" <jn******@pacbello.net> wrote in message
news:pa***************************@pacbello.net...
| On Tue, 28 Feb 2006 19:56:44 -0500, Peter_Julian wrote:
|
| >
| > "Jay_Nabonne" <jn******@pacbello.net> wrote in message
| > news:pa****************************@pacbello.net.. .
| > | On Sun, 26 Feb 2006 17:34:58 -0800, Daniel wrote:
| > |

<snip>

|
| Since the function call is through the A*, there is only one
| function signature to examine (the one in A). As it turns out, if the
| virtual function in C had a different return type, it wouldn't compile
| anyway, so my suggestion to check the return type doesn't help here. But
| the rest are valid. For example, this code prints "B" because the function
| "f" in class C doesn't match the signature of the base class:
|
| class A
| {
| public:
| virtual void f() = 0;
| };
|
| class B : public A
| {
| virtual void f()
| {
| printf("B\n");
| }
| };
|
| class C : public B
| {
| void f() const
| {
| printf("C\n");
| }
| };
|
| int main()
| {
| A* p = new C; // undefined behaviour
| p->f();
| return 0;
| }
|
| - Jay

Non-const member functions have a default this parameter. Adding the const
modifier simply strips away the this parameter. So f() in C does not have
the same signature. Its got nothing to do with the return type.

Every cycle through the above program will leak memory since delete p was
never called to invoke C's d~tor (of course, debug mode may help depending
on your debugger). If you still don't think thats undefined behaviour: write
a function that leaks a new allocation and loop through it a few thousand
times in release mode.

Anything could happen, it may format your hard drive, it might crash your
OS, it may do nothing else except lower your resources. That, in effect, is
the definition of undefined behaviour.
Mar 5 '06 #18

P: n/a
On Sun, 05 Mar 2006 02:11:19 -0500, Peter_Julian wrote:

<snip>

| A* p = new C; // undefined behaviour

Please explain. I don't think I'm asking for much. Just justification for
your statements. :)

Non-const member functions have a default this parameter. Adding the const
modifier simply strips away the this parameter. So f() in C does not have
the same signature. Its got nothing to do with the return type.
I don't recall saying it did. I was pointing out (hopefully for the OP's
benefit) that having difference of const can cause the behavior he was
seeing. (I once helped someone solve exactly that problem, which is why
I'm pointing it out.)

Every cycle through the above program will leak memory since delete p was
never called to invoke C's d~tor (of course, debug mode may help depending
on your debugger). If you still don't think thats undefined behaviour: write
a function that leaks a new allocation and loop through it a few thousand
times in release mode.
I don't think it's "undefined behavior" in the strict C++ sense, no. If
there is a place in the C++ specification that contradicts that, then I'm
more than willing to change my thoughts.

Anything could happen, it may format your hard drive, it might crash your
OS, it may do nothing else except lower your resources. That, in effect, is
the definition of undefined behaviour.


There's "undefined behavior" in the English sense, and there's "undefined
behavior" in the C++ sense. Cases of the latter are explictly called out
in the specification. Again, please state where a memory leak is called
out as C++ undefined behavior in the specification.

I've said all I plan to on this (short of retracting my statements if
disproved by the specification). You seem to be dodging my requests for
you to validate your statements, and I don't see any gain to asking you
any further.

- Jay
Mar 6 '06 #19

P: n/a
On Sun, 05 Mar 2006 02:11:19 -0500, Peter_Julian wrote:

One more thought...

Every cycle through the above program will leak memory since delete p was
never called to invoke C's d~tor (of course, debug mode may help depending
on your debugger). If you still don't think thats undefined behaviour: write
a function that leaks a new allocation and loop through it a few thousand
times in release mode.

Anything could happen, it may format your hard drive, it might crash your
OS, it may do nothing else except lower your resources. That, in effect, is
the definition of undefined behaviour.


Consider the following code:

#include <list>

typedef std::list<unsigned char*> MyList;

int main()
{
MyList l;

// Allocate the world.
try
{
for (;;)
{
l.push_back(new unsigned char[1024]);
}
}
catch(...)
{
}

// Release the world.
for (MyList::iterator i = l.begin(); i != l.end(); ++i)
{
delete [] *l;
}
}

This code will (in theory) use up a tremendous amount of memory, only
stopping when it is no longer able to allocate memory. Following the logic
you expressed above, since it's unclear what the OS will do when it gets
low on memory, then the above code exhibits "undefined behavior" (I'm
setting up the same conditions, more or less, that the offending
non-delete code had). It could, according to you, format the hard drive.

And yet (assuming I coded it right off the top of my head) all new's are
paired with delete's. So how to identify the "undefined behavior"?

Again, even though the effect of the above code on an OS is not
necessarily predictable, it is not "undefined behavior" as defined by the
C++ standard. So the effect of a memory leak on the OS is immaterial since
the above code, which has no undefined behavior as defined by the C++ spec
duplicates the conditions you described.

- Jay

Mar 6 '06 #20

P: n/a

"Jay_Nabonne" <jn******@pacbello.net> wrote in message
news:pa****************************@pacbello.net.. .
| On Sun, 05 Mar 2006 02:11:19 -0500, Peter_Julian wrote:
|
| One more thought...
|
| >
| > Every cycle through the above program will leak memory since delete p
was
| > never called to invoke C's d~tor (of course, debug mode may help
depending
| > on your debugger). If you still don't think thats undefined behaviour:
write
| > a function that leaks a new allocation and loop through it a few
thousand
| > times in release mode.
| >
| > Anything could happen, it may format your hard drive, it might crash
your
| > OS, it may do nothing else except lower your resources. That, in effect,
is
| > the definition of undefined behaviour.
|
| Consider the following code:
|
| #include <list>
|
| typedef std::list<unsigned char*> MyList;
|
| int main()
| {
| MyList l;
|
| // Allocate the world.
| try
| {
| for (;;)
| {
| l.push_back(new unsigned char[1024]);
| }
| }
| catch(...)
| {
| }
|
| // Release the world.
| for (MyList::iterator i = l.begin(); i != l.end(); ++i)
| {
| delete [] *l;
| }
| }
|
| This code will (in theory) use up a tremendous amount of memory, only
| stopping when it is no longer able to allocate memory. Following the logic
| you expressed above, since it's unclear what the OS will do when it gets
| low on memory, then the above code exhibits "undefined behavior" (I'm
| setting up the same conditions, more or less, that the offending
| non-delete code had). It could, according to you, format the hard drive.

Not..

A computer that appropriately allocates and deallocates *all* of its memory
is low in resources at that very moment, thats all. Your arguement is not
the same. There is a huge difference between a program that runs out of
allocation space and one that permanently leaks it. The former renders a
known machine state and the latter does not.

As you've found out, the try block unwinds the stack only. That try block,
and much less a program, is unable to recover from a memory leak if you do
not deallocate the elements (the pointers would be lost forever).
Once the pointers are lost, no deallocation is possible unless you implement
placement new and an allocation pool (which is what debug mode does).

Or do i need to remind you that deallocating the pointer itself from the
stack does not deallocate the newed pointee?

Let me artificially induce a simulated memory allocation failure for you
after the tenth allocation.
Go ahead, comment out the %deallocations% section and then explain to me how
thats not undefined behaviour. That, after all, has been your arguement
until now.
____
#include <iostream>
#include <ostream>
#include <vector>
#include <list>
#include <stdexcept>

class N
{
int m_n;
std::vector<int> vn;
public:
N(int n) : m_n(n), vn(1024, 0)
{
if (m_n > 9) throw std::bad_alloc("out of mem !!!");
std::cout << "\nN()";
}
~N() { std::cout << "\n~N()"; }
};

int main()
{
std::list< N* > ln;
try
{
// allocations
for (int i = 0;; ++i)
{
ln.push_back(new N(i));
}
}
catch (const std::exception& e)
{
std::cout << "\nerror: " << e.what();
}

// size()
std::cout << "\n# of elements in list = " << ln.size();

// deallocations
typedef std::list< N* >::iterator LIter;
LIter liter = ln.begin();
for( liter; liter != ln.end(); ++liter)
{
delete *liter;
}

return 0;
}

/*

N()
N()
N()
N()
N()
N()
N()
N()
N()
N()
error: out of mem !!!
# of elements in list = 10
~N()
~N()
~N()
~N()
~N()
~N()
~N()
~N()
~N()
~N()

*/
____

|
| And yet (assuming I coded it right off the top of my head) all new's are
| paired with delete's. So how to identify the "undefined behavior"?

The program does not produce undefined behaviour since deallocation takes
place. You answered your own question. However, you've missed a critical
point... what if class N was a derived class?

Yes, thats right, what if we didn't modify main() in any way and derived
from a base class? hmm?

|
| Again, even though the effect of the above code on an OS is not
| necessarily predictable, it is not "undefined behavior" as defined by the
| C++ standard. So the effect of a memory leak on the OS is immaterial since
| the above code, which has no undefined behavior as defined by the C++ spec
| duplicates the conditions you described.
|
| - Jay
|

Thats not so, the programs you have described until now are ones without
deallocations. This program does not leak resources. Thanks, but you've
basicly proven the point i was making.

Now, lets start dealing with the real issue: shall we? I like to stick to my
guns.

If class N above were a derived class and its base class did not have a
virtual d~tor, deallocating the class N elements would leak the base class
component at each iteration. That's the undefined behaviour i was alluding
to.

Try it! add a base class with a non-virtual destructor, derive class N from
it, and test the theory with output in the base class d~tors. Pay attention.
I'm talking of a derived N *with* deallocation.

Hmm, undefined behaviour takes a whole new meaning then, doesn't it? delete
pointer is incapable of invoking the base class d~tor unless its virtual.
Thats a fact that needs to be viewed with great respect in C++ since
debugging for it when the client calls is a nightmare.

That is why, for example, deriving from the STL containers is not
recommended. Their destructors are not virtual.
Ya learn something every day, thats what makes life cool.

Mar 7 '06 #21

This discussion thread is closed

Replies have been disabled for this discussion.