Templated containers of inherited objects

G

George Exarchakos

Hi everyone,

I'd like your help...

Can we have a std::list<BASE> where BASE be the base class of a class
hierarchy? I want to add to this list objects that are inherited from
BASE class but not necessarily the same...

class base {
int x;
};

class child1 : public base {
int d;
};

class child2 : public base {
int e;
};

int main() {
std::list<base> mylist;
child1 _c1;
child2 _c2;
mylist.push_back(_c1);
mylist.push_back(_c2);
return 0;
}

It seems that I lose the information of _c1 and _c2 like they are
converted to base.

Please, do you have any idea how can I achieve that? Do I have to
implement my own container?

Many thanks in advance,
-- George
 
R

red floyd

George said:
Can we have a std::list<BASE> where BASE be the base class of a class
hierarchy? I want to add to this list objects that are inherited from
BASE class but not necessarily the same...

> [redacted]
>
Please, do you have any idea how can I achieve that? Do I have to
implement my own container?

You've run into the slicing problem. You need to create a container of
(smart) pointers to your base class.

That is, std::list<base*>, or std::list<some_smart_ptr<base> >

See the FAQ, http://www.parashift.com/c++-faq-lite/containers.html#faq-34.4
 
G

George Exarchakos

George said:
Can we have a std::list<BASE> where BASE be the base class of a class
hierarchy? I want to add to this list objects that are inherited from
BASE class but not necessarily the same...
[redacted]

Please, do you have any idea how can I achieve that? Do I have to
implement my own container?

You've run into the slicing problem. You need to create a container of
(smart) pointers to your base class.

That is, std::list<base*>, or std::list<some_smart_ptr<base> >

See the FAQ,http://www.parashift.com/c++-faq-lite/containers.html#faq-34.4

Many thanks Red Floyd! This tip is exactly what I was looking for...
-- George
 
R

Roland Pibinger

Can we have a std::list<BASE> where BASE be the base class of a class
hierarchy? I want to add to this list objects that are inherited from
BASE class but not necessarily the same...

STL does not support object-oriented programming. STL containers,
iterators and algorithms are designed only for values, not for
(pointers to) objects. Workarounds exist but with 'Standard' STL there
always remains an 'impedance mismatch' when objects are used.

Best regards,
Roland Pibinger
 
P

paul.joseph.davis

STL does not support object-oriented programming.

I think that this comment is misleading. If I understand the point of
the statement, its that using objects that use polymorphism in a
container isn't supported. Because this isn't the case at all.
Although, objects used with the STL must provide certain concepts,
such as default constructibility, copy constructibility et al.

When dealing with class hierarchies, we must pay special attention to
copy constructibility and other concepts because these can lead to
problems such as slicing as noticed by the OP.

Using pointers is a perfectly valid method of fixing this issue.

On the other hand, if Roland meant that "You can't subclass objects
such as std::vector." then he is in fact correct. IIRC, most stl
objects don't provide a virtual destructor. And further, the STL (as
its name suggests) is a library of templates. Using templates with
inheritance is at best tricky.

I'm not sure what the actual standard has to say on this, but g++
4.0.2 won't compile this:

class A
{
public:
A() {}
~A() {}

template< typename TYPE >
virtual void
echo( TYPE val ) = 0 ;
} ;

The error is:
error: invalid use of 'virtual' in template declaration of 'virtual
void A::echo(TYPE)'

This all makes sense if we think in terms of the (non-standard
dictated) vtable implementation of polymorphism. When we create the
vtable for A, we have no idea how many entries it will have.
Theoretically, it would need to have an entry for every template
instantiation of A::echo(TYPE).

Whats more, this class definition could possibly change after it has
been compiled. Ie, compiled in a library, then some client code calls
A::echo(TYPE) with a type thats not used in the library. Thus the
vtable would change and break all sorts of things.

STL containers,
iterators and algorithms are designed only for values, not for
(pointers to) objects.

This is just wrong. A pointer *is* a value. Its a memory address. STL
containers will work just fine with pointers.

Objects on the other hand must provide the required concepts as
mentioned before. The entire point of the STL is to provide a system
that allows for use with any custom type. They'd be pretty pointless
if they could only hold things like int, float, char* et al.

Workarounds exist but with 'Standard' STL there
always remains an 'impedance mismatch' when objects are used.

I'm not entirely certain what 'impedance mismatch' means. Although it
kinda reminds me of studying power amplifiers...

But the point that needs to be made here is that using smart pointers
in a container is not a work around. Smart pointers are a means to
perform resource control. In the case of using pointers, if the only
copy of a pointer to an object is in a std::vector, and then
std::vector::clear() is called, then the objects in it are leaked
(assuming we don't have copies to the pointer anywhere else). Smart
pointers do reference counting ( or some other method ) to determine
when an object should be delete'ed. So when std::vector::clear() is
called, the smart pointers can determine whether or not the object
should be delete'ed and thuse, no memory is leaked.
Best regards,
Roland Pibinger

HTH,
Paul Davis
 
G

Gavin Deane

On the other hand, if Roland meant that "You can't subclass objects
such as std::vector." then he is in fact correct. IIRC, most stl
objects don't provide a virtual destructor.

That doesn't stop me deriving from them.
#include <vector>
class foo : public std::vector<int> {};

Nothing wrong there. The only thing I can't do without a virtual
destructor is delete polymorphically.

Whether deriving from standard containers is a good idea or not is a
different question, but there is certainly nothing that prevents it.

Gavin Deane
 
K

Kai-Uwe Bux

Gavin said:
That doesn't stop me deriving from them.
#include <vector>
class foo : public std::vector<int> {};

Nothing wrong there. The only thing I can't do without a virtual
destructor is delete polymorphically.

Whether deriving from standard containers is a good idea or not is a
different question, but there is certainly nothing that prevents it.

Except that, formally, an implementation of std::vector<> that prevents
derivation (using a virtual base class trick getting) is standard
compliant. In practice, all implementations of the standard library allow
for derivation; and I do not hesitate to derive from standard container
classes whenever it feels appropriate. However, I do not see any blessing
for this technique in the standard. As far as I can tell, this would be a
compliant implementation:

namespace std {

class __protected_constructor {
protected:

__protected_constructor ( void ) {}

}; // protected_constructor

template < typename T, typename A >
class vector : private virtual __protected_constructor {

// ... do the vector business.

}; // vector

}


Best

Kai-Uwe Bux
 
R

Roland Pibinger

I think that this comment is misleading. If I understand the point of
the statement, its that using objects that use polymorphism in a
container isn't supported.

Just look at the containers, iterators and algorithms. No algorithm
works with (pointers to) objects (except random_shuffle).
Because this isn't the case at all.
Although, objects used with the STL must provide certain concepts,
such as default constructibility, copy constructibility et al.
When dealing with class hierarchies, we must pay special attention to
copy constructibility and other concepts because these can lead to
problems such as slicing as noticed by the OP.

Objects (in the meaning used in object-oriented programing) are
characterized by identity, state and behavior. It makes no sense to
copy or assign objects. Copy constructibility makes only sense for
values and value-oriented libraries like STL.
Using pointers is a perfectly valid method of fixing this issue.

If it needs fixing it must be broken.
STL containers,

This is just wrong. A pointer *is* a value. Its a memory address. STL
containers will work just fine with pointers.

A pointer is not a value, otherwise one could just use an int instead.
A pointer in C/C++ has the combined meaning of reference and address.
Objects on the other hand must provide the required concepts as
mentioned before.

The 'required concepts' make sense only for values, not objects.
The entire point of the STL is to provide a system
that allows for use with any custom type. They'd be pretty pointless
if they could only hold things like int, float, char* et al.

.... and 'concrete types' as Stroustrup calls them.
Workarounds exist but with 'Standard' STL there

I'm not entirely certain what 'impedance mismatch' means. Although it
kinda reminds me of studying power amplifiers...

It means the mismatch that becomes evident when you try to use objects
with the value-based STL library.
But the point that needs to be made here is that using smart pointers
in a container is not a work around. Smart pointers are a means to
perform resource control.

Since 'smart pointers' only try to imitate real pointers the
'impedance mismatch' is the same.

In general, STL is the attempt introduce the functional paradigm into
the C++ language as counterpart to the object-oriented paradigm. This
attempt has produced much misunderstanding and confusion.

Best wishes,
Roland Pibinger
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
... formally, an implementation of std::vector<> that prevents
derivation (using a virtual base class trick getting) is standard
compliant.

Sorry, that's incorrect.

§17.3.1.2/1 "The library can be extended by a C++ program. ... Such
extensions are generally one of the following: ... Derived classes."
 
P

paul.joseph.davis

Just look at the containers, iterators and algorithms. No algorithm
works with (pointers to) objects (except random_shuffle).

This examples shows how to use std::sort on a std::vector or pointers.
As well as a trivial example of std::for_each to print the vector.

#include <iostream>
#include <vector>

class A
{
public:
A( int val )
{
_val = val ;
}

~A() {}

int
value()
{
return _val ;
}

private:

int _val ;
} ;

bool
compare( A* lhs, A* rhs )
{
return lhs->value() < rhs->value() ;
}

void
echo( A* a )
{
std::cout << a->value() << std::endl ;
}

int
main( int argc, char* argv[] )
{
std::vector< A* > vec ;

for( int i = 10 ; i > 0 ; i-- )
{
A* a = new A( i ) ;
vec.push_back( a ) ;
}

std::cout << "Before sort:" << std::endl ;
std::for_each( vec.begin(), vec.end(), echo ) ;

std::sort( vec.begin(), vec.end(), compare ) ;

std::cout << "After sort:" << std::endl ;
std::for_each( vec.begin(), vec.end(), echo ) ;
}
Objects (in the meaning used in object-oriented programing) are
characterized by identity, state and behavior. It makes no sense to
copy or assign objects. Copy constructibility makes only sense for
values and value-oriented libraries like STL.

You're right, wrong, and neither right or wrong. Of course objects are
characterized by their state and behavior. You're absolutely wrong
that it makes no sense to copy or assign objects, there are instances
where this may be true, but its just plain wrong to say never. I use
it quite a bit.

And the last bit that copy constructibility only makes sense in the
STL isn't quite right, although I don't tend to use it alot in my own
code, thats mostly becuase I don't write code that requires this
concept.
If it needs fixing it must be broken.



A pointer is not a value, otherwise one could just use an int instead.
A pointer in C/C++ has the combined meaning of reference and address.

You can use an int for a pointer! (Disclaimer: Its very ill advised,
bad C++, and requires that an int be the same size as the pointer you
wish to obfuscate.)
The 'required concepts' make sense only for values, not objects.


... and 'concrete types' as Stroustrup calls them.



It means the mismatch that becomes evident when you try to use objects
with the value-based STL library.


Since 'smart pointers' only try to imitate real pointers the
'impedance mismatch' is the same.

I'm unsure as to how I should respond to this.

Yes, smart pointers only imitate real pointers. They are actually
objects that use a little voodoo to track the resources they own (The
underlying raw pointer). Because they are objects, they can be copied
and assigned. Writing code without smart pointers is ill-advised in my
opinion.

The only 'mismatch' that occurs with the objects used in the STL is
when the object doesn't support something required of it. The
algorithms in the STL were designed to work with any user defined type
as long as those types support a few basic operations. Obviously, if
something doesn't meet these requirements it won't work. You'll find
this happens alot when requirements aren't met.
In general, STL is the attempt introduce the functional paradigm into
the C++ language as counterpart to the object-oriented paradigm. This
attempt has produced much misunderstanding and confusion.

Obviously the STL can be misunderstood...
 
P

paul.joseph.davis

* Kai-Uwe Bux:




Sorry, that's incorrect.

§17.3.1.2/1 "The library can be extended by a C++ program. ... Such
extensions are generally one of the following: ... Derived classes."

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Yeah, I was completely wrong on this point, as the following example
illustrates:

#include <iostream>
#include <vector>

template< typename TYPE >
class A : public std::vector<TYPE>
{
public:
A() {}
} ;

int
main( int argc, char* argv[] )
{
A<int> a ;
a.push_back( 0 ) ;
std::cout << a.size() << std::endl ;
}
 
K

Kai-Uwe Bux

Alf said:
* Kai-Uwe Bux:

Sorry, that's incorrect.

No need to be sorry: if I was incorrect here, I would be quite happy.
§17.3.1.2/1 "The library can be extended by a C++ program. ... Such
extensions are generally one of the following: ... Derived classes."

You may be right (and I hope you are). However, I feel uneasy relying on
that clause alone.

a) Formally, the trick presented does not prevent derivation. It just
prevents construction of objects of the derived type:

namespace std {

class __protected_constructor {
protected:

__protected_constructor ( void ) {}

}; // protected_constructor

template < typename T, typename A >
class vector : private virtual __protected_constructor {

// ... do the vector business.

}; // vector

}

You still can do

class X : public std::vector<int> {
}


you just cannot do

X x;

anymore. Where do you find a provision that classes derived from
std::vector<> have to behave they way you want it to be? Without specific
guarantees that extensions are actually useful, the clause you quote
amounts to very little.


b) This is somewhat recognized in the part of the clause that you snipped:

The library can be extended by a C++ program. Each clause, as applicable,
describes the requirements that such extensions must meet. Such extensions
are generally one of the following:
? Template arguments
? Derived classes
? Containers, iterators, and/or algorithms that meet an interface convention

I read the sentence "Each clause, as applicable, describes the requirements
that such extensions must meet." as a hint that more specific provisions
are supposed to determine the semantics (behavior) of extensions. In the
absence of such provisions, I do not see how the standard places any
requirements on the behavior of a program extending the library. I.e., I
see a lot of undefined behavior. 17.3.1.2 in itself clearly does not say
how things are supposed to behave.


c) On the other hand, in favor of your reading, I can add that library
components such as

unary_function<>

which are clearly meant to be derived from, come without any hint whatsoever
that derivation is ok. However, the are other example, too: clause
[24.3.2/1] specifically blesses the use of std::iterator<> as a base class.


d) Finally, a question: consider a type such as std::vector::iterator that
is labelled as implementation_defined, does the extendibility clause apply
to it, too? In that case, std::vector<T>::iterator could not be implemented
as T*.



Anyway, I would be _really_ _really_ happy if I was wrong and derivation
from standard containers was blessed by the standard. Also I think that (c)
is actually quite strong a point in your direction. But I think, the
standard could be more clear in this regard.



Thanks

Kai-Uwe Bux
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
Alf said:
* Kai-Uwe Bux:
Sorry, that's incorrect.

No need to be sorry: if I was incorrect here, I would be quite happy.
:)

§17.3.1.2/1 "The library can be extended by a C++ program. ... Such
extensions are generally one of the following: ... Derived classes."

You may be right (and I hope you are). However, I feel uneasy relying on
that clause alone.

[snip]
Without specific
guarantees that extensions are actually useful, the clause you quote
amounts to very little.

How would you define "useful"?

Anywayhow, I've argued successfully earlier that any program whatsoever
is a 100% standard-conforming C++ compiler, by the formally interpreted
rules of the standard. That's because the standard isn't a mathematical
document, but relies on the reader's common sense: it's formal in
places, but only insofar as formal definitions are practical and really
communicate something. So you can rejoice ;-), the standard does
specify, to the extent it can, that std::vector can be derived from.
 
?

=?iso-8859-1?q?Kirit_S=E6lensminde?=

A pointer is not a value, otherwise one could just use an int instead.
A pointer in C/C++ has the combined meaning of reference and address.

What is your definition of 'value' that makes you say that a pointer
isn't a value? After all a pointer *is* just an integer, but one which
is interpreted in a specific way (old x86 style far pointers
notwithstanding), but that is the same as many other value types too.
In general, STL is the attempt introduce the functional paradigm into
the C++ language as counterpart to the object-oriented paradigm. This
attempt has produced much misunderstanding and confusion.

I've come across this assertion a couple of times. To me the STL is a
fine bit of OO programming, but I can see where the functional view
comes into it too.

The use of generic containers and the ability to perform operations no
them has been part of both functional languages and OO languages up
until early C++ and Java. What the STL doesn't do though is to enforce
the 'no side effect' rule from pure functional programming languages.
To me this puts it firmly on the OO side. It isn't really doing
anything that the containers in Smalltalk don't do and I'm not sure
that anybody would claim them to be a functional paradigm.


K
 
R

Roland Pibinger

This examples shows how to use std::sort on a std::vector or pointers.
As well as a trivial example of std::for_each to print the vector.

#include <iostream>
#include <vector>

class A
{
public:
A( int val )
{
_val = val ;
}

~A() {}

int
value()
{
return _val ;
}

private:

int _val ;
} ;

bool
compare( A* lhs, A* rhs )
{
return lhs->value() < rhs->value() ;
}

This is the workaround. As said before no algorithm (except
random_shuffle) works with pointers out of the box.
void
echo( A* a )
{
std::cout << a->value() << std::endl ;
}

int
main( int argc, char* argv[] )
{
std::vector< A* > vec ;

for( int i = 10 ; i > 0 ; i-- )
{
A* a = new A( i ) ;
vec.push_back( a ) ;
}

// compiles but wrong result
std::sort( vec.begin(), vec.end()) ;

// ditto; 2 dereferences needed to access the object
for (std::vector<A*>::iterator i = vec.begin(); i != vec.end(); ++i) {
std::cout << *i << std::endl;
}

const std::vector<A*>& const_vec = vec;
// changing pointed-to element in const vector
*const_vec[0] = 3;

// changing pointed-to element through const_iterator
// 2 dereferences needed to access the object.
**const_vec.begin() = 4;
std::cout << "Before sort:" << std::endl ;
std::for_each( vec.begin(), vec.end(), echo ) ;

std::sort( vec.begin(), vec.end(), compare ) ;

std::cout << "After sort:" << std::endl ;
std::for_each( vec.begin(), vec.end(), echo ) ; // leaking objects
}

You're right, wrong, and neither right or wrong. Of course objects are
characterized by their state and behavior. You're absolutely wrong
that it makes no sense to copy or assign objects, there are instances
where this may be true, but its just plain wrong to say never. I use
it quite a bit.

When identity is important (which is the case with objects) then
duplicate instances are always problematic (not to speak of
assignment).

Best wishes,
Roland Pibinger
 
R

Roland Pibinger

What is your definition of 'value' that makes you say that a pointer
isn't a value? After all a pointer *is* just an integer, but one which
is interpreted in a specific way (old x86 style far pointers
notwithstanding), but that is the same as many other value types too.

A pointer can be dereferenced, i.e. pointers have a specific
dereference operator.
I've come across this assertion a couple of times. To me the STL is a
fine bit of OO programming, but I can see where the functional view
comes into it too.

OO is characterized by inherited and polymorphic objects. STL neither
uses nor supports these.
The use of generic containers and the ability to perform operations no
them has been part of both functional languages and OO languages up
until early C++ and Java.

The term 'generic' doesn't tell you anything about the underying
paradigm.
What the STL doesn't do though is to enforce
the 'no side effect' rule from pure functional programming languages.

STL is not pure functional. Otherwise sort, for_each and the other
'mutating' algorithms would have to return a list of immutable values.
But it is influenced by functional programming. The term 'generic' is
just a distraction in this context.
To me this puts it firmly on the OO side.

Side effects don't necessarily mean OO :)

Best wishes,
Roland Pibinger
 
K

Kai-Uwe Bux

Roland said:
A pointer can be dereferenced, i.e. pointers have a specific
dereference operator.


OO is characterized by inherited and polymorphic objects. STL neither
uses nor supports these.

Hm, I always felt that this is simply because pointers are not really what
we want when dealing with objects. What we want is something like a
reference that can be assigned and re-seated. Something like this:

template < typename T >
class smart_ref {

T * the_ptr;

public:

// maybe, we should not have this constructor !
smart_ref ( T * ptr )
: the_ptr ( ptr )
{}

smart_ref ( T & ref )
: the_ptr ( &ref )
{}

operator T & ( void ) {
return ( *the_ptr );
}

operator T const & ( void ) const {
return ( *the_ptr );
}

// these really want to be overloaded dot-operators:
T * operator-> ( void ) {
return ( the_ptr );
}

T const * operator-> ( void ) const {
return ( the_ptr );
}

};

With this, STL works to some extend. The amount of failure derives mostly
from the fact that we cannot overload the dot-operator (which is a real
shame).


#include <vector>
#include <algorithm>
#include <iostream>
#include <string>
#include <ostream>

class X {

unsigned int the_ssn;

public:

X ( unsigned int ssn )
: the_ssn ( ssn )
{}

virtual
std::string name ( void ) const {
return ( "X" );
}

unsigned int ssn ( void ) const {
return ( the_ssn );
}

~X ( void ) {}

friend
bool operator< ( X const & lhs, X const & rhs ) {
return ( lhs.the_ssn < rhs.the_ssn );
}

};

struct Y : public X {

Y ( unsigned int ssn )
: X ( ssn )
{}

virtual
std::string name ( void ) const {
return ( "Y" );
}

virtual
~Y ( void ) {}

};

std::eek:stream & operator<< ( std::eek:stream & ostr,
X const & x ) {
return ( ostr << x.name() << "(" << x.ssn() << ")" );
}

typedef smart_ref<X> X_ref;
typedef std::vector<X_ref> X_vect;

int main ( void ) {
{
X a ( 333451234 );
X b ( 919113247 );
Y c ( 238648666 );
Y d ( 237986136 );
X e ( 879823769 );
X_vect xv;
xv.push_back( X_ref(a) );
xv.push_back( X_ref(b) );
xv.push_back( X_ref(c) );
xv.push_back( X_ref(d) );
xv.push_back( X_ref(e) );
std::sort( xv.begin(), xv.end() );
for ( X_vect::const_iterator iter = xv.begin();
iter != xv.end(); ++iter ) {
std::cout << *iter << '\n';
}
}
}


Maybe, it's not that STL does not want to work with OO-objects. Maybe, it's
just that pointers make for lousy OO-objects.


Best

Kai-Uwe Bux
 
R

Roland Pibinger

Hm, I always felt that this is simply because pointers are not really what
we want when dealing with objects. What we want is something like a
reference that can be assigned and re-seated. Something like this:

template < typename T >
class smart_ref {

T * the_ptr;

public:

// maybe, we should not have this constructor !
smart_ref ( T * ptr )
: the_ptr ( ptr )
{}

smart_ref ( T & ref )
: the_ptr ( &ref )
{}

operator T & ( void ) {
return ( *the_ptr );
}

operator T const & ( void ) const {
return ( *the_ptr );
}

// these really want to be overloaded dot-operators:
T * operator-> ( void ) {
return ( the_ptr );
}

T const * operator-> ( void ) const {
return ( the_ptr );
}

};

With this, STL works to some extend.

The problem with this approach is the automatic conversion from
pointer to reference (operator T&) which produces all kinds of
surprising results. BTW, Boost has a similar library (just for the
sake of completeness, not that I recommend Boost).
Moreover, this approach tackles the problem from the wrong direction.
Containers, iterators and algorithms need to become 'pointer-aware'.
This is not too difficult and has been done in the past.
The amount of failure derives mostly
from the fact that we cannot overload the dot-operator (which is a real
shame).

IMO, pointers and references should be unified in a (hypothetical) new
C++. The result would be references similar (but not identical) to
Java.

Best wishes,
Roland Pibinger
 
?

=?iso-8859-1?q?Kirit_S=E6lensminde?=

A pointer can be dereferenced, i.e. pointers have a specific
dereference operator.

I still don't see how that stops it from being a value. Are you saying
that pointers don't follow value semantics because they can also be de-
referenced? In which case why is that relevant? Or are you saying they
do follow value semantics despite not being values?
OO is characterized by inherited and polymorphic objects. STL neither
uses nor supports these.

OO is characterised by encapsulated objects that have internal state,
are responsible for their own behaviour and communicate through
message passing. The STL does follow this. This newer definition of OO
centred around inheritance is a direct result of Java and to a lesser
extent C++, but it has no historic basis.

I've explained this in more detail no my web site:
http://www.kirit.com/The three pillars of Object Orientation


K
 
J

Jerry Coffin

[ ... ]
OO is characterised by encapsulated objects that have internal state,
are responsible for their own behaviour and communicate through
message passing. The STL does follow this. This newer definition of OO
centred around inheritance is a direct result of Java and to a lesser
extent C++, but it has no historic basis.

I see little basis for this claim. Consider, for example:

Object-oriented programming is a method of implementation in
which programs are orgainzed as cooperative collections of
objects, each of which represents an instance of some class,
and whose classes are all members of a hierarchy of classes
united via inheritance relationships.

That's from _Object-Oriented Analysis and Design: With Applications
(Second Edition)_, Grady Booch, 1994. In the same book, use of objects
but not inheritance is titled "Object-based".

Going back even further:

A subclass specifies that its instances will be the same
as instances of another class, called its _superclass_,
except for the differences that are explicitly stated.
The Smalltalk-80 programmer always creates a new class as
a subclass of an existing class. A system class named
Object describes the similarity of all objects in the
system, so every class will at least be a subclass of
Object.

_Smalltalk-80: The Language_, Adele Goldberg and David Robson, 1989.

There is little or no historical support for the notion that use of
objects in the absence of inheritance gives object oriented programming.
The Standard Template Library was (and remains) largely template-based
-- but C++ was considered an object-oriented programming language long
before it supported templates at all.
 

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,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top