472,798 Members | 1,162 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,798 software developers and data experts.

Virtual Methods Question

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
20 1929

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

"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

"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

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

"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

<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
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

"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

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

"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
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
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

"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

"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
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
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

"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
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
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

"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 thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

20
by: Raymond Lewallen | last post by:
I read this on this website page http://www.vbip.com/books/1861004915/chapter_4915_06.asp: Unlike many object-oriented languages, all methods in VB.NET are virtual. Now in BOL, Under...
3
by: Milan Cermak | last post by:
Hi all, I have sort of philosophical question. Contructor and destructor behavior points me to this. Is table of virtual methods held in object? Do I get it right that every created object has...
9
by: jlopes | last post by:
There seems to bet no diff between a vitual method and an inheirited method. class A_Base { public: virtual void filter(){ /* some code */ } }; class D_of_A_Base : public A_Base {
4
by: aap | last post by:
Hi, I have the following code. #include <iostream> using namespace std; class Root { public: virtual void Root1() = 0; virtual void Root2() = 0;
32
by: Adrian Herscu | last post by:
Hi all, In which circumstances it is appropriate to declare methods as non-virtual? Thanx, Adrian.
4
by: Rafael Veronezi | last post by:
I have some questions about override in inheritance, and virtual members. I know that you can you override a method by two ways in C#, one, is overriding with the new keyword, like: public new...
15
by: John Salerno | last post by:
Hi all. I have a question about virtual and override methods. Please forgive the elementary nature! First off, let me quote Programming in the Key of C#: "Any virtual method overridden with...
2
by: Heinz Ketchup | last post by:
Hello, I'm looking to bounce ideas off of anyone, since mainly the idea of using Multiple Virtual Inheritance seems rather nutty. I chalk it up to my lack of C++ Experience. Here is my...
7
by: Markus Svilans | last post by:
Hello, My question involves virtual functions and inheritance. Suppose we have a class structure, that consists of "data" classes, and "processor" classes. The data classes are derived from...
4
by: David Zha0 | last post by:
Hi, "when we call a virtual method, the runtime will check the instance who called the method and then choose the suitable override method, this may causes the performance drop down", is this...
0
linyimin
by: linyimin | last post by:
Spring Startup Analyzer generates an interactive Spring application startup report that lets you understand what contributes to the application startup time and helps to optimize it. Support for...
0
by: erikbower65 | last post by:
Here's a concise step-by-step guide for manually installing IntelliJ IDEA: 1. Download: Visit the official JetBrains website and download the IntelliJ IDEA Community or Ultimate edition based on...
0
by: Taofi | last post by:
I try to insert a new record but the error message says the number of query names and destination fields are not the same This are my field names ID, Budgeted, Actual, Status and Differences ...
14
DJRhino1175
by: DJRhino1175 | last post by:
When I run this code I get an error, its Run-time error# 424 Object required...This is my first attempt at doing something like this. I test the entire code and it worked until I added this - If...
0
by: Rina0 | last post by:
I am looking for a Python code to find the longest common subsequence of two strings. I found this blog post that describes the length of longest common subsequence problem and provides a solution in...
5
by: DJRhino | last post by:
Private Sub CboDrawingID_BeforeUpdate(Cancel As Integer) If = 310029923 Or 310030138 Or 310030152 Or 310030346 Or 310030348 Or _ 310030356 Or 310030359 Or 310030362 Or...
0
by: lllomh | last post by:
How does React native implement an English player?
0
by: Mushico | last post by:
How to calculate date of retirement from date of birth
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.