The C++ Object Model: Good? Bad? Ugly?

T

tonytech08

What I like about the C++ object model: that the data portion of the
class
IS the object (dereferencing an object gets you the data of a POD
object).

What I don't like about the C++ object model: that most OO features
are not
available for class object design without loss of POD-ness.

So, I'm more than leaning toward "bad" because of the limitations and
that the language doesn't distinguish what is of very key importance.
How do you feel about the object model?
 
J

James Kanze

What I like about the C++ object model: that the data portion
of the class IS the object (dereferencing an object gets you
the data of a POD object).

No it doesn't.
What I don't like about the C++ object model: that most OO
features are not available for class object design without
loss of POD-ness.
So, I'm more than leaning toward "bad" because of the
limitations and that the language doesn't distinguish what is
of very key importance. How do you feel about the object
model?

The "object model" in the C++ standard is very low-level.
Intentionally. It is designed for you to build on. The object
model of the classes you write is for you to design. It can be
more OO than many other languages (e.g. Java or C#), when that's
appropriate. Just as it can drop the OO model completely when
that's appropriate (e.g value objects). The essential point of
C++ is that it doesn't impose any application level object
model; it lets you use whatever is appropriate.
 
S

Salt_Peter

What I like about the C++ object model: that the data portion of the
class
IS the object (dereferencing an object gets you the data of a POD
object).

Definitely not. Data is just data. The true power of the C++ Object
Model is the object's behaviour and extensability.
What I don't like about the C++ object model: that most OO features
are not
available for class object design without loss of POD-ness.

So, I'm more than leaning toward "bad" because of the limitations and
that the language doesn't distinguish what is of very key importance.
How do you feel about the object model?

Objects sometimes need to do more than just store data. Behaviour is
nice to have. That inludes 'behaviour' at construction and expected
behaviours through a secure interface. It makes maintaining and
extending code easy, simple.

Write a program that uses some given polymorphic type hierarchy and
have your customer invent / add some new improved class of his own.
The customer's new class works with your original program without
changing a single line of code (except maybe an include).

You can't do that with PODs only. Whats nice is you can bend the
language to choose what path you prefer.
 
T

tonytech08

Hi
I really don't get completely, what you mean, but for me, the
important things about C++ object model are simplicity, efficiency and
extenability. For example the representation of concrete classes like
Date in memory is exactly like a POD Date struct.

"Concrete class"? You are using that to mean "struct".
The size of object
of a derived class is the size of base class sub-object and its data
members. The size of an object of a class with virtual functions
increases just by the size of virtual v-ptr.

That's "the problem" I implied: that quickly the object model destroys
the concept of a class looking like the data portion in memory.
 
T

tonytech08

No it doesn't.

class SomePODClass
{
public:
int first_int;
int second_int;

void Func1(){}
};

SomePODClass X;
int first_int_in_obj = *(int*)&X; // this is not pretty, but true
The "object model" in the C++ standard is very low-level.
Intentionally.  It is designed for you to build on.  The object
model of the classes you write is for you to design.  It can be
more OO than many other languages (e.g. Java or C#), when that's
appropriate.  Just as it can drop the OO model completely when
that's appropriate (e.g value objects).  The essential point of
C++ is that it doesn't impose any application level object
model; it lets you use whatever is appropriate.

It does restrict usage of OO concepts unless one jettisons the "value
object" concept, which is unfortunate (and unnecessary?).
 
T

tonytech08

Definitely not. Data is just data. The true power of the C++ Object
Model is the object's behaviour and extensability.

Depends on how you want to view it. Sure, class = data+behavior, but I
wasn't talking about fundamental "definitions" but rather usage of
class objects, one aspect of which is manipulating the data directly
for, say for example, in a memory management scenario. That one has to
be severely restricted from using OO concepts without affecting the
representation in memory of the data portion, is unfortunate. I'm not
sure how much could be or could have been done to make the situation
better. For example, was it really necessary to disallow constructors
with initialization lists? I mean, no vptr needed for that!
Objects sometimes need to do more than just store data. Behaviour is
nice to have. That inludes 'behaviour' at construction and expected
behaviours through a secure interface. It makes maintaining and
extending code easy, simple.

Write a program that uses some given polymorphic type hierarchy and
have your customer  invent / add some new improved class of his own.
The customer's new class works with your original program without
changing a single line of code (except maybe an include).

You can't do that with PODs only. Whats nice is you can bend the
language to choose what path you prefer.

That's only one usage scenario though. A LOT of usefull stuff can be
built without heavy class objects "weighed down" with vptrs and other
stuff. I of course am referring to that other scenario so talking
about the scenario you mentioned isn't relevant.
 
J

James Kanze

class SomePODClass
{
  public:
   int first_int;
   int second_int;
   void Func1(){}
};
SomePODClass X;
int first_int_in_obj = *(int*)&X; // this is not pretty, but true

But it's only true for POD types, and only because of
constraints of C compatiblity. The data portion of the class
isn't the object in general.
It does restrict usage of OO concepts unless one jettisons the
"value object" concept, which is unfortunate (and
unnecessary?).

It restricts the use of OO concepts to classes designed to be
used with OO concepts. It supports non OO concepts as well,
which are more appropriate in some cases.
 
T

tonytech08

But it's only true for POD types,

Well that's why I added the parenthetical part in my original post: to
make clear I was referring to what I like about the C++ model and wish
it wouldn't get abberated so quickly by changing the memory
representation of the data portion. 'POD' was very key for my thought,
though could have been worded better to show that I guess.
and only because of
constraints of C compatiblity.  The data portion of the class
isn't the object in general.

I tend to think of the data portion (noun, vs. behavior=verb) as "the
thing" because that's what get's operated on and maybe even directly
manipulated. I'm not willing to go to a paradigm that only allows data
manipulation via class methods. That is way to high level and
constraining for me. A lot of people complain about C and C++ because
of the memory management, but for me, that's one of the things I like
about it! GC breaks MY programming model for example.
It restricts the use of OO concepts to classes designed to be
used with OO concepts.  

Not really, since one can have POD classes with methods, just not
CERTAIN methods (you are suggesting that "classes designed to be used
with OO concepts" are those heavyweight classes that break PODness,
right?). You seem to be saying that POD classes are not supported or
at least not encouraged. That would be a real downer if true. I'd like
to see more support in the langauge for POD classes. I don't know how
much can be implemented before it becomes impossible. Certainly
initializing constructors can be had? Polymorphism not, but only NOT
because of the way C++ implements it?
 
I

Ian Collins

tonytech08 said:
Not really, since one can have POD classes with methods, just not
CERTAIN methods (you are suggesting that "classes designed to be used
with OO concepts" are those heavyweight classes that break PODness,
right?).

What's a "heavyweight class"?
You seem to be saying that POD classes are not supported or
at least not encouraged.

Where? They are supported, a C struct is also a C++ struct.
That would be a real downer if true. I'd like
to see more support in the langauge for POD classes. I don't know how
much can be implemented before it becomes impossible. Certainly
initializing constructors can be had? Polymorphism not, but only NOT
because of the way C++ implements it?
An instance of a polymorphic class has to contain information about its
type, so it can't be the same as a POD class. Constructors and
(non-virtual) methods are irrelevant.
 
J

James Kanze

Well that's why I added the parenthetical part in my original
post: to make clear I was referring to what I like about the
C++ model and wish it wouldn't get abberated so quickly by
changing the memory representation of the data portion. 'POD'
was very key for my thought, though could have been worded
better to show that I guess.

Well, PODs make up part of the C++ object model; one important
aspect of it is that PODs do behave differently from objects
which have non-trivial constructors or destructors. The main
reasons for this are probably linked with C compatibility, but
it was always intended in C++ that you could create simple
structures which were no more than a collection of data (with or
without member functions), as well as full OO type objects. The
object model of C++ is designed to support both.
I tend to think of the data portion (noun, vs. behavior=verb)
as "the thing" because that's what get's operated on and maybe
even directly manipulated.

In C++ (and even in C, for that matter), an object has a type
and an address; the type determines its size, and the set of
legal operations on it. Since an object is a thing, in some
way, I guess it is a noun, but even a POD struct has behavior:
you can assign it, for example, or access members. Compared to
C, C++ adds the ability for the user to define additional
operations (member functions), and to define non-trivial
initialization and destruction (which forces significant changes
in the object model). Beyond that, C++ adds support for dynamic
typing (which is what one usually understands with OO).

[...]
Not really, since one can have POD classes with methods, just
not CERTAIN methods (you are suggesting that "classes designed
to be used with OO concepts" are those heavyweight classes
that break PODness, right?).

No. I'm really not suggesting much of anything. However you
define the concept of OO, the concept only applies to classes
which were designed with it in mind. C++ doesn't force any
particular OO model, but allows you to chose. And to have
classes which aren't conform to this model.
You seem to be saying that POD classes are not supported or at
least not encouraged.

Where do I say that? POD classes are definitely supported, and
are very useful in certain contexts. They aren't appropriate
for what most people would understand by OO, but so what. Not
everything has to be rigorously OO.
That would be a real downer if true. I'd like to see more
support in the langauge for POD classes.

Such as? Part of the motivation for defining POD as a special
category is C compatibility; a POD should be usable in C.
Beyond that, there is a wide range of things you can do.
I don't know how much can be implemented before it becomes
impossible. Certainly initializing constructors can be had?

A non-trivial constructor causes changes in the way object
lifetime is defined. So the results aren't (and can't be) a
POD. The justification is simple: you can't define an object
with a non-trivial constructor in C.
Polymorphism not, but only NOT because of the way C++
implements it?

Polymorphism implies a non-trivial constructor. Regardless of
how it is implemented, something must occur (some code must
execute) for the raw memory to assume its type.

The next version of the standard adds some additional
definitions; in addition to PODs and agglomerates, it has
something called standard-layout structs and unions, and support
for managing alignment. But even without that, I've found
plenty of support for everything I've had to do, at all levels.
 
J

James Kanze

[...]
An instance of a polymorphic class has to contain information
about its type, so it can't be the same as a POD class.
Constructors and (non-virtual) methods are irrelevant.

Just a nit, but it's possible to implement polymorphism with no
additional information in the instance itself; you could, for
example, maintain the information is a separate hashtable which
mapped the address to the type information. What isn't possible
is to establish this mapping without executing some code. I.e. a
constructor.
 
T

tonytech08

What's a "heavyweight class"?

A non-POD class.
Where?  They are supported, a C struct is also a C++ struct.

"Not supported adequately" is what I meant.
An instance of a polymorphic class has to contain information about its
type, so it can't be the same as a POD class.

Because of the way C++ implements polymorphism: tacking on a vptr to
the class data.
 Constructors and
(non-virtual) methods are irrelevant.

Apparently relevant: a POD can't have "non-trivial" constructors, for
one thing.
 
T

tonytech08

Well, PODs make up part of the C++ object model; one important
aspect of it is that PODs do behave differently from objects
which have non-trivial constructors or destructors.  The main
reasons for this are probably linked with C compatibility, but
it was always intended in C++ that you could create simple
structures which were no more than a collection of data (with or
without member functions), as well as full OO type objects.  The
object model of C++ is designed to support both.

Thanks for reiterating my thought: C++ has more support for OO with
"full OO type objects".
In C++ (and even in C, for that matter), an object has a type
and an address; the type determines its size, and the set of
legal operations on it.  Since an object is a thing, in some
way, I guess it is a noun, but even a POD struct has behavior:
you can assign it, for example, or access members.  Compared to
C, C++ adds the ability for the user to define additional
operations (member functions), and to define non-trivial
initialization and destruction (which forces significant changes
in the object model).  Beyond that, C++ adds support for dynamic
typing (which is what one usually understands with OO).

Not sure what your point is. I said that I consider the data portion
of an object, "the object". I wasn't trying to be implementation
literal about it. Yes, data+behavior= class, but when the
implementation starts adding things to the data portion, that defines
a different animal than a POD class.
    [...]
Not really, since one can have POD classes with methods, just
not CERTAIN methods (you are suggesting that "classes designed
to be used with OO concepts" are those heavyweight classes
that break PODness, right?).

No.  I'm really not suggesting much of anything.  However you
define the concept of OO, the concept only applies to classes
which were designed with it in mind.  C++ doesn't force any
particular OO model, but allows you to chose.  And to have
classes which aren't conform to this model.

"Allows you to choose"? "FORCES you to choose" between lightweight
(POD) class design with more limited OO and and heavyweight (non-POD)
class design with all OO mechanisms allowed but at the expense of
losing POD-ness. It's a compromise. I'm not saying it's a bad
compromise, but I am wondering if so and what the alternative
implementation possibilities are.
Where do I say that?  POD classes are definitely supported, and
are very useful in certain contexts.  They aren't appropriate
for what most people would understand by OO, but so what.  Not
everything has to be rigorously OO.

You seemed to imply that the "supported" ("ecouraged" would probably
be a better word to use) paradigms were: A. data structs with non-
trivial member functions and built-in "behavior" and B. "full OO type
objects".
Such as?  

What I call "initializing constructors" for one thing. (Constructors
that take arguments to initialize a POD class in various ways).
Part of the motivation for defining POD as a special
category is C compatibility; a POD should be usable in C.
Beyond that, there is a wide range of things you can do.

Can't construct conveniently as you can with heavyweight class
objects. Why allowing this would break POD-ness escapes me. Perhaps it
would break C-compatibility? Maybe defining POD-ness as "C
compatibility of structs" is a hindrance, if it is defined something
like that.
A non-trivial constructor causes changes in the way object
lifetime is defined.  So the results aren't (and can't be) a
POD.  The justification is simple: you can't define an object
with a non-trivial constructor in C.

So it is just the backward compatibility with C that prevents having
those nifty constructors?
Polymorphism implies a non-trivial constructor.  Regardless of
how it is implemented, something must occur (some code must
execute) for the raw memory to assume its type.

The more obvious issue with the polymorphism implementation is the
vptr. But I'm not too worried about not being able to derive from POD-
classes (for now anyway).
 
T

tonytech08

    [...]
An instance of a polymorphic class has to contain information
about its type, so it can't be the same as a POD class.
Constructors and (non-virtual) methods are irrelevant.

Just a nit, but it's possible to implement polymorphism with no
additional information in the instance itself; you could, for
example, maintain the information is a separate hashtable which
mapped the address to the type information.  What isn't possible
is to establish this mapping without executing some code. I.e. a
constructor.

And how relatively (relative to the current implementation) slow would
that be is the question. I'm pretty much doing that for GUI code
(windowing). I'm not sure that it's appriate at the language
implementation level as a technique though (?).
 
J

James Kanze

[...]
An instance of a polymorphic class has to contain
information about its type, so it can't be the same as a
POD class. Constructors and (non-virtual) methods are
irrelevant.
Just a nit, but it's possible to implement polymorphism with
no additional information in the instance itself; you could,
for example, maintain the information is a separate
hashtable which mapped the address to the type information.
What isn't possible is to establish this mapping without
executing some code. I.e. a constructor.
And how relatively (relative to the current implementation)
slow would that be is the question.

I don't think anyone actually knows, since as far as I know, no
one has actually implemented it this way. A virtual table
look-up involves two indirections---read the vptr, then the vtbl
itself; it seems pretty obvious that a hash table implementation
can't come close. It might be useful, however, for
instrumenting the code, in order to generate various statistics,
or even doing some additional error checking on the side
(although I'm not sure what).

As I said above, my comment was just a nit---it's possible to
implement polymorphism with no information about the type in the
class object itself. Conceptually, at least; it's probably not
practical.
 
J

James Kanze

Thanks for reiterating my thought: C++ has more support for OO
with "full OO type objects".

More support than what. C++ has support for "full OO type
objects", if that's what you need. Most of my objects aren't
"full OO type objects", in the sense that they don't support
polymorphism. C++ supports them just as well.
Not sure what your point is. I said that I consider the data
portion of an object, "the object".

But that's simply wrong, at least in the C++ object model. An
object has a type. Otherwise, it's just raw memory. That's a
fundamental principle of any typed language.
I wasn't trying to be implementation literal about it. Yes,
data+behavior= class, but when the implementation starts
adding things to the data portion, that defines a different
animal than a POD class.

But the implementation *always* adds things to the data portion,
or controls how the data portion is interpreted. It defines a
sign bit in an int, for example (but not in an unsigned int).
If you want to support signed arithmetic, then you need some way
of representing the sign. If you want to support polymorphism,
then you need some way of representing the type. I don't see
your point. (The point of POD, in the standard, is C
compatibility; anything in a POD will be interpretable by a C
compiler, and will be interpreted in the same way as in C++.)
[...]
It restricts the use of OO concepts to classes designed to
be used with OO concepts.
Not really, since one can have POD classes with methods,
just not CERTAIN methods (you are suggesting that "classes
designed to be used with OO concepts" are those
heavyweight classes that break PODness, right?).
No. I'm really not suggesting much of anything. However you
define the concept of OO, the concept only applies to classes
which were designed with it in mind. C++ doesn't force any
particular OO model, but allows you to chose. And to have
classes which aren't conform to this model.
"Allows you to choose"? "FORCES you to choose" between
lightweight (POD) class design with more limited OO and and
heavyweight (non-POD) class design with all OO mechanisms
allowed but at the expense of losing POD-ness. It's a
compromise. I'm not saying it's a bad compromise, but I am
wondering if so and what the alternative implementation
possibilities are.

Obviously, you have to choose the appropriate semantics for the
class. That's part of design, and is inevitable. So I don't
see your point; C++ gives you the choice, without forcing you
into any one particular model. And there aren't just two
choices.
You seemed to imply that the "supported" ("ecouraged" would
probably be a better word to use) paradigms were: A. data
structs with non- trivial member functions and built-in
"behavior" and B. "full OO type objects".

Not at all. You define what you need. From a design point of
view, I find that it rarely makes sense to mix models in a
single class: either all of the data will be public, or all of
it will be private. But the language doesn't require it.
What I call "initializing constructors" for one thing.
(Constructors that take arguments to initialize a POD class in
various ways).

Well, if there is a non-trivial constructor, the class can't be
POD, since you need to call the constructor in order to
initialize it. Anything else would be a contradiction: are you
saying you want to provide a constructor for a class, but that
it won't be called? That doesn't make sense.
Can't construct conveniently as you can with heavyweight class
objects. Why allowing this would break POD-ness escapes me.

Because you can't have a constructor in C, basically. Because
the compiler must generate code when the object is created, if
there is a constructor. That is what POD-ness is all about; a
POD object doesn't require any code for it to be constructed.
Perhaps it would break C-compatibility?

Certainly, since you couldn't instantiate an instance of the
object in C.
Maybe defining POD-ness as "C compatibility of structs" is a
hindrance, if it is defined something like that.

The next version of the standard does have an additional
category "layout compatible". I'm not sure what it buys us,
however.
So it is just the backward compatibility with C that prevents
having those nifty constructors?

Interface compatibility. And if you're not interfacing with C,
you can have them. The object won't be a POD, but if you're not
using it to interface with C, who cares?
The more obvious issue with the polymorphism implementation is
the vptr. But I'm not too worried about not being able to
derive from POD- classes (for now anyway).

But you can derive from a POD class. A pointer to a POD class
just won't behave polymorphically.
 
J

Jerry Coffin

(e-mail address removed)>, (e-mail address removed)
says...

[ ... ]
I don't think anyone actually knows, since as far as I know, no
one has actually implemented it this way. A virtual table
look-up involves two indirections---read the vptr, then the vtbl
itself; it seems pretty obvious that a hash table implementation
can't come close. It might be useful, however, for
instrumenting the code, in order to generate various statistics,
or even doing some additional error checking on the side
(although I'm not sure what).

As I said above, my comment was just a nit---it's possible to
implement polymorphism with no information about the type in the
class object itself. Conceptually, at least; it's probably not
practical.

I can think of a way that might be practical. Allocate objects in pools
of (say) one megabyte (of virtual address space) apiece. All objects in
a given pool are of the same type. A vtable for that pool of objects is
located at the beginning of the one megabyte range, so you can get from
an object to its vtable by anding out the bottom 20 bits of the object's
address.

This could waste a little bit of address space if you had more than one
megabyte of a given type of object, but _could_ also be somewhat faster
than the usual current implementation -- it substitutes a CPU operation
(usually quite fast) for a memory access (often a lot slower).
 
J

James Kanze

(e-mail address removed)>, (e-mail address removed)
says...
[ ... ]
I don't think anyone actually knows, since as far as I know,
no one has actually implemented it this way. A virtual
table look-up involves two indirections---read the vptr,
then the vtbl itself; it seems pretty obvious that a hash
table implementation can't come close. It might be useful,
however, for instrumenting the code, in order to generate
various statistics, or even doing some additional error
checking on the side (although I'm not sure what).
As I said above, my comment was just a nit---it's possible
to implement polymorphism with no information about the type
in the class object itself. Conceptually, at least; it's
probably not practical.
I can think of a way that might be practical. Allocate objects
in pools of (say) one megabyte (of virtual address space)
apiece. All objects in a given pool are of the same type. A
vtable for that pool of objects is located at the beginning of
the one megabyte range, so you can get from an object to its
vtable by anding out the bottom 20 bits of the object's
address.
This could waste a little bit of address space if you had more
than one megabyte of a given type of object, but _could_ also
be somewhat faster than the usual current implementation -- it
substitutes a CPU operation (usually quite fast) for a memory
access (often a lot slower).

It would also be very difficult to make it work with static and
local objects; you'd probably need a fall-back solution for
those.
 

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,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top