struct question

E

Eric

This IS material from a CS class on object oriented programming. It is
NOT my homework.

Consider the following:

struct A {short i; void f () {cout << "A::f()\n";}};
struct B : A {long j; void f () {cout << "B::f()\n";}
void g () {cout << "B::g()\n";}};
{
A* const a = new B[10]; // dangerous (1)
a[0].f(); // A::f();
// a[1].f(); // undefined (2)
// delete [] a; // undefined (3)
}

I have questions about three lines, which are marked.

(1): Why might this be considered dangerous?
(2): This line is commented out and marked as undefined. And yet, when I
remove the comment, it compiles and runs, producing the same output as
a[0].f(), that is, A::f(). Why is this?
(3): Again, though this is marked as undefined, when I uncomment it, it
executes fine.

I haven't been able to introduce complications. Why then, might these
lined be marked as such?

Thanks,
Eric
 
E

Eric

Here is the same code from the program, ready to be copy/pasted and
compiled:

#include <iostream>
using namespace std;

struct A {short i; void f () {cout << "A::f()\n";}};
struct B : A {long j; void f () {cout << "B::f()\n";}
void g () {cout << "B::g()\n";}};
int main () {
A* const a = new B[10]; // dangerous (1)
a[0].f(); // A::f();
// a[1].f(); // undefined (2)
// delete [] a; // undefined (3)
return 0;
}
 
N

Noah Roberts

Eric said:
This IS material from a CS class on object oriented programming. It is
NOT my homework.

Consider the following:

struct A {short i; void f () {cout << "A::f()\n";}};
struct B : A {long j; void f () {cout << "B::f()\n";}
void g () {cout << "B::g()\n";}};
{
A* const a = new B[10]; // dangerous (1)
a[0].f(); // A::f();
// a[1].f(); // undefined (2)
// delete [] a; // undefined (3)
}

I have questions about three lines, which are marked.

(1): Why might this be considered dangerous?
(2): This line is commented out and marked as undefined. And yet, when I
remove the comment, it compiles and runs, producing the same output as
a[0].f(), that is, A::f(). Why is this?
(3): Again, though this is marked as undefined, when I uncomment it, it
executes fine.

I haven't been able to introduce complications. Why then, might these
lined be marked as such?

prob 1: Your objects are not polymorphic but you treat them as such.
prob 2: You're attempting to store objects polymorphically in an array.

I'll leave it to you to see how those two problems answer your three
questions.
 
A

Alan Johnson

Eric said:
This IS material from a CS class on object oriented programming. It is
NOT my homework.

Consider the following:

struct A {short i; void f () {cout << "A::f()\n";}};
struct B : A {long j; void f () {cout << "B::f()\n";}
void g () {cout << "B::g()\n";}};
{
A* const a = new B[10]; // dangerous (1)
a[0].f(); // A::f();
// a[1].f(); // undefined (2)
// delete [] a; // undefined (3)
}

I have questions about three lines, which are marked.

(1): Why might this be considered dangerous?
(2): This line is commented out and marked as undefined. And yet, when I
remove the comment, it compiles and runs, producing the same output as
a[0].f(), that is, A::f(). Why is this?
(3): Again, though this is marked as undefined, when I uncomment it, it
executes fine.

I haven't been able to introduce complications. Why then, might these
lined be marked as such?

Thanks,
Eric

Let's make some assumptions about your platform. Whether these are true
for your specific platform isn't really relevant:
1) A "short" is 2 bytes.
2) A "long" is 4 bytes.
3) There are no padding bytes added in struct A or struct B.

Consider what gets created in memory (with the above assumptions) when
"new B[10]" is executed. If we represent each byte with the name of
the variable it is a part of, and separate each object by a '|', it
looks something like:

iijjjj | iijjjj | iijjjj | iijjjj | iijjjj | iijjjj | iijjjj | iijjjj |
iijjjj | iijjjj

Now, as I'm sure you know, when 'a' is a pointer, a[x] is essentially
just shorthand for *(a+x). Under our assumptions above, sizeof(struct
A) is 2. So, given that knowledge, try to identify which object is
identified by a[1] in our crude picture of memory.

This should be enough for you to be able to answer your questions.
 
S

Salt_Peter

Eric said:
This IS material from a CS class on object oriented programming. It is
NOT my homework.

Consider the following:

struct A {short i; void f () {cout << "A::f()\n";}};
struct B : A {long j; void f () {cout << "B::f()\n";}
void g () {cout << "B::g()\n";}};
{
A* const a = new B[10]; // dangerous (1)
a[0].f(); // A::f();
// a[1].f(); // undefined (2)
// delete [] a; // undefined (3)
}

I have questions about three lines, which are marked.

(1): Why might this be considered dangerous?
(2): This line is commented out and marked as undefined. And yet, when I
remove the comment, it compiles and runs, producing the same output as
a[0].f(), that is, A::f(). Why is this?
(3): Again, though this is marked as undefined, when I uncomment it, it
executes fine.

I haven't been able to introduce complications. Why then, might these
lined be marked as such?

Thanks,
Eric

They are marked as such because they are wrong.

(1) is wrong because you are allocating a non-polymorphicly derived
type (x10) and storing that array's address in a pointer to Base A
(which, incidently, thinks that you stored 10 bases). Imagine the mess.
Its not just dangerous - its destructive.

your array, logicly looks like this (where each letter is the
corresponding memory allocated):

ABABABABABABABABABAB

and your pointers is absolutely convinced that it has:

AAAAAAAAAA

a[0].f() happens to work by pure luck since its the first that was
allocated in that fake pointer's index.
(2) is wrong in the sense that B:f() is expected but polymorphism has
been denied by the coder. The result of that statement is undefined.
And it IS undefined because the pointer is unaware that the array is
actually an array of B's. If you had attempted to access a member
variable with A::f(), the program would have crashed on the spot.
Obviously,the second element of base A is not were the pointer thinks
it is.

(3) is nasty because because that dumb pointer proceeds to calculate
the size of base A, multiplies it by 10 and attempts to deallocate only
a portion of the aforementioned array allocation. And thats twice as
nasty because each time the destructor for A is invoked (except the
first), its invoked on something that is not an A or for that matter
anything at all (so the member short i is never released). Not to
mention that the destructors of B were not even attempted. You just
leaked into memory the entire allocation except one instance of base A.

Why don't you declare and define you ctors and d~tors with std::cout
outputs? Deallocation is a requirement and absolutely critical in this
language.
 
E

Eric

Thank you for your reply. What I know and a little reading on
polymorphism helped me understand this much better.
Why don't you declare and define you ctors and d~tors with std::cout
outputs? Deallocation is a requirement and absolutely critical in this
language.

I'm not sure I understand where cout fits into this.
 
S

Salt_Peter

Eric said:
Thank you for your reply. What I know and a little reading on
polymorphism helped me understand this much better.


I'm not sure I understand where cout fits into this.

Constructors and destructors are pivotal in this language, the compiler
has no choice but to generate them if you don't anyways. Specially when
using polymorphism and developing new theories, give the compiler a
chance to give you feedback about whats happening. Also, instead of
creating a container of one million objects, get one object to work
correctly first.

#include <iostream>
#include <memory>

struct A
{
A() : i(0) { std::cerr << "A()\n"; } // def ctor
virtual ~A() { std::cerr << "~A()\n"; } // d~tor, polymorphic
/* member functions */
virtual void f() const // virtual member function
{
std::cerr << "A::f()\n";
std::cout << "i = " << i;
std::cout << std::endl;
}
private:
short i;
};

struct B : public A
{
B() : j(0) { std::cerr << "B()\n"; }
~B() { std::cerr << "~B()\n"; } // automatically virtual
void f() const // automatically virtual
{
std::cerr << "B::f()\n";
std::cout << "j = " << j;
std::cout << std::endl;
}
private:
long j;
};

int main()
{
std::auto_ptr< A > p_a( new B() ); // same as new/delete except its
automatic
p_a->f();

return 0;
}

/* output if A is polymorphic
A()
B()
B::f()
j = 0
~B() // std::auto_ptr goes out of scope, invokes d~tors polymorphicly
~A()
*/

/* without virtuals
A()
B()
A::f() // wrong function called !!!
i = 0
~A()
// mem leak, B() is never destroyed
*/

You'll understand that the result of removing the virtual keywords is
much more dangerous than you think in your program. Its incorrect *and*
catastrophic.
Without polymorphism, imagine what happens if A::f() attempts to access
short i in the second element, which is not of type A, if you had an
array?
The consequences are dramatic.
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top