Object Management

S

spekyuman

I found a public paper online stating, "The first time I returned to
writing C++ after a year of writing Java, I was appalled at how much
my design was constrained by managing the lifetime of objects. When C+
+ classes share objects, then they must negotiate who owns the object.
Garbage collection is not available, and smart pointers often fall
short." This is how the fellow introduced himself. He goes on to make
some design decisions for object management:

[No pointers as arguments.]

Pass all objects to class methods and constructors as references.
There is absolutely no advantage to passing objects as pointers. This
rule is euqally valid whether the objects are const or not.

I recommend that all class members be saved as pointers. You can
easily take the address of an argument reference with an ampersand and
assign it to your member pointer. Some C++ programmers do not seem to
realize that the address of a references is the same as the address of
the original object. So they pass pointers when they want to save the
argument, and references when they do not. This is a poor form of
documentation, based on misunderstanding.

If an object is passed to a constructor or initialization method, the
user can expect the class to hang onto it. If a method saves an object
from an argument, choose an appropriate name like setColor(Color&) or
addInterpolator(Interpolator&).

The worst excuse for using a pointer as an argument is that you want
to give it a default value of null. You still have to document what a
null object is supposed to mean. Worse, the user may overlook that the
argument exists or is optional. Declare a separate method that lacks
the extra argument. The effort is negligible.

QUESTION: Should all class members be implemented as pointers? Why or
why not?
QUESTION: Should all parameters be implemented as references? Why or
why not?

[Returning objects.]

One can always return objects from class methods by reference, either
const or non-const. A user can take the address of the reference, if
necessary, to save the object. But there are no drawbacks to returning
objects always as pointers. Consistency is preferable, and most API
return pointers.

If you return an object allocated on the heap with the new operator,
the be clear who has ownership of the object--its class, the
recipient, or a third-party.

Think about whether you are breaking encapsulation of member data in a
way that will prevent modification later.

Never return a reference to a class member allocated on the stack in
the header file. If your class replaces the value, then the user may
be left with an invalid reference, even though your object still
exists. Your class will never be able to remove the object as a
member. A user may manipulate the logic of your class in unexpected
ways.

A method should modify an object constructed by the user by accepting
it as a non-const reference. Returning the same object would be
redundant and confusing.

QUESTION: "One can always return objects from class methods by
reference, either const or non-const. A user can take the address of
the reference, if necessary, to save the object. There are no
drawbacks to returning objects always as pointers." (Referring to the
statement that all objects should be implemented with pointer
members.) Are there any drawbacks when this is the case?

How should members and their operations be handled in a general sense,
while considering effeciency and portability top priority?
 
S

spekyuman

In a 'general sense', you are in the wrong NG. You want a 'design' NG.

FAQ http://www.parashift.com/c++-faq-lite
( see section 5 for suggestions)

yes, parashift is amazing, good reference, visit it frequently...
however, i must insist that these questions are related to this
newsgroup in every sense... the questions listed above require c++
language, compile-time knowledge and merely question the correct usage
of these language semantics... lets see if you cannot reference
parashift and answer my question now?
 
B

BobR

spekyuman said:
yes, parashift is amazing, good reference, visit it frequently...
however, i must insist that these questions are related to this
newsgroup in every sense... the questions listed above require c++
language, compile-time knowledge and merely question the correct usage
of these language semantics... lets see if you cannot reference
parashift and answer my question now?

Profile!

Provide some context for your questions. 'in general' gets an 'in general'
answer (like I suggested).


No. Because it isn't required in all cases. (POD struct/class).

No. Because it isn't required in all cases. (POD struct/class).

I can't answer that one, "Homey don't play that!". I use references where I
can, and pointers if I have to. Context?

"always" is a hang-up. You never do "always" in C++ design (....only for
standard conformance).

Answers are my *opinion*. Refer to the 'standard' for solid answers.
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

I found a public paper online stating, "The first time I returned to
writing C++ after a year of writing Java, I was appalled at how much
my design was constrained by managing the lifetime of objects. When C+
+ classes share objects, then they must negotiate who owns the object.
Garbage collection is not available, and smart pointers often fall
short." This is how the fellow introduced himself. He goes on to make
some design decisions for object management:

The question about who owns an object have nothing to do with pointers
references or such. It's a problem you can run into in Java too, you
just don't have to deal with deleting pointers.
[No pointers as arguments.]

Pass all objects to class methods and constructors as references.
There is absolutely no advantage to passing objects as pointers. This
rule is euqally valid whether the objects are const or not.

I recommend that all class members be saved as pointers. You can
easily take the address of an argument reference with an ampersand and
assign it to your member pointer. Some C++ programmers do not seem to
realize that the address of a references is the same as the address of
the original object. So they pass pointers when they want to save the
argument, and references when they do not. This is a poor form of
documentation, based on misunderstanding.

If an object is passed to a constructor or initialization method, the
user can expect the class to hang onto it. If a method saves an object
from an argument, choose an appropriate name like setColor(Color&) or
addInterpolator(Interpolator&).

The worst excuse for using a pointer as an argument is that you want
to give it a default value of null. You still have to document what a
null object is supposed to mean. Worse, the user may overlook that the
argument exists or is optional. Declare a separate method that lacks
the extra argument. The effort is negligible.

QUESTION: Should all class members be implemented as pointers? Why or
why not?

Certainly not, why keep a pointer to an int when you can have the int
itself?
QUESTION: Should all parameters be implemented as references? Why or
why not?

Of course not, if you do you can't make assignment work for some objects.

If you are not aware of the differences between normal members, pointers
and references then it's time to start studying. My personal preference
is to avoid pointers if possible since there are certain problems only
pointers can create. That does not mean that I don't use them however.
[Returning objects.]

One can always return objects from class methods by reference, either
const or non-const. A user can take the address of the reference, if
necessary, to save the object. But there are no drawbacks to returning
objects always as pointers. Consistency is preferable, and most API
return pointers.

If you return an object allocated on the heap with the new operator,
the be clear who has ownership of the object--its class, the
recipient, or a third-party.

Think about whether you are breaking encapsulation of member data in a
way that will prevent modification later.

Never return a reference to a class member allocated on the stack in
the header file. If your class replaces the value, then the user may
be left with an invalid reference, even though your object still
exists. Your class will never be able to remove the object as a
member. A user may manipulate the logic of your class in unexpected
ways.

A method should modify an object constructed by the user by accepting
it as a non-const reference. Returning the same object would be
redundant and confusing.

QUESTION: "One can always return objects from class methods by
reference, either const or non-const. A user can take the address of
the reference, if necessary, to save the object. There are no
drawbacks to returning objects always as pointers." (Referring to the
statement that all objects should be implemented with pointer
members.) Are there any drawbacks when this is the case?

That's simply not true, sometimes you have to return temporaries and if
you do that using a pointer or reference you'll get into deep trouble.
How should members and their operations be handled in a general sense,
while considering effeciency and portability top priority?

If you know the difference between passing/returning by value,
reference, and pointer it is often quite obvious when you need to pass/
return by value (i.e. a copy of the object). In the remaining cases you
can usually use either a pointer or a reference, my advice is to use a
reference when you can and a pointer when you must.
 
S

spekyuman

Found some interesting article on parashift earlier. Please, share
your expertise. Article 31 states:

Reference semantics are A Good Thing. We can't live without pointers.
We just don't want our software to be One Gigantic Rats Nest Of
Pointers. In C++, you can pick and choose where you want reference
semantics (pointers/references) and where you'd like value semantics
(where objects physically contain other objects etc). In a large
system, there should be a balance. However if you implement absolutely
everything as a pointer, you'll get enormous speed hits.

Objects near the problem skin are larger than higher level objects.
The identity of these "problem space" abstractions is usually more
important than their "value." Thus reference semantics should be used
for problem-space objects.

Note that these problem space objects are normally at a higher level
of abstraction than the solution space objects, so the problem space
objects normally have a relatively lower frequency of interaction.
Therefore C++ gives us an ideal situation: we choose reference
semantics for objects that need unique identity or that are too large
to copy, and we can choose value semantics for the others. Thus the
highest frequency objects will end up with value semantics, since we
install flexibility where it doesn't hurt us (only), and we install
performance where we need it most!

///////////////////////////////////////////////////////////////

Observation #1 Implementing all member objects using pointers and
references, in large systems, will impact performance.

Observation #2 Objects with higher levels of abstraction generally
require less amounts of space, which makes sense, because they have
not undergone significant amounts of specialization.

Observation #3 Therefore, in large systems, it makes most sense to
construct member objects using the following logics:

(1) A higher frequency of interactivity suggests that member objects
should/could be constructed using value semantics.

(2) A low frequency of interactivity suggests that member objects
should/could be constructed using reference semantics, inlucding the
case where member objects are overly large, or when they require their
identity to remain ultimately uncompromised, modified or unmodified.

Observation #4 Member objects that comprise larger hierarchies,
envoking inheritance and polymorphic mechanisms, should be passed by
reference for dynamic binding.

///////////////////////////////////////////////////////////////

Essentially, extensive indirection will impact performance and
requires careful consideration of the above observations. Therefore,
reference semantics, figuratively, have the ability to clog
inheritance and polymorphic mechanisms.

Generalizing:

Parameters should be passed by reference for dynamic binding,
maintains polymorphic flexibilities.

Parameters should be passed by value for primitives and/or relatively
simplistic objects.

Member objects should be constructed using value semantics, unless
requirements dictate otherwise; avoiding figurative clogging.

Please, keep this constructive. :)
 
J

Jerry Coffin

[ ... ]
[No pointers as arguments.]

Pass all objects to class methods and constructors as references.
There is absolutely no advantage to passing objects as pointers. This
rule is euqally valid whether the objects are const or not.

At least the last sentence is correct: the rule is equally valid under
various circumstances -- because it's not valid under any circumstances.

Sometimes you need a pointer. Other times you need a reference. If you
have a reasonable choice, prefer references -- but that certainly
doesn't mean you should always use them.
I recommend that all class members be saved as pointers.

This is lunacy. In reality, rather the opposite is true: a class should
contain at most one member that's a pointer, and when it does, the class
should be dedicated to managing that pointer. If it's not dedicated to
managing a pointer, a class shouldn't contain a pointer -- it should (at
most) contain instances of other classes that manage pointers on its
behalf.

Of course, quite a few things simply don't need to be pointers at all --
directly, or managed by another class. Storing a pointer where you
really only need a char would be incredibly foolish and wasteful.
You can
easily take the address of an argument reference with an ampersand and
assign it to your member pointer. Some C++ programmers do not seem to
realize that the address of a references is the same as the address of
the original object. So they pass pointers when they want to save the
argument, and references when they do not. This is a poor form of
documentation, based on misunderstanding.

This seems to be a complete nonsequiter and justifies nothing.

[ ... ]
The worst excuse for using a pointer as an argument is that you want
to give it a default value of null. You still have to document what a
null object is supposed to mean. Worse, the user may overlook that the
argument exists or is optional. Declare a separate method that lacks
the extra argument. The effort is negligible.

The effort may be negligible, but appears to accomplish nothing -- using
overloading instead of a default argument makes no real difference in
the documentation required. Either way you need to document the number
and types of parameters allowed and what is accomplished with different
numbers of parameters.
[Returning objects.]

One can always return objects from class methods by reference, either
const or non-const. A user can take the address of the reference, if
necessary, to save the object. But there are no drawbacks to returning
objects always as pointers. Consistency is preferable, and most API
return pointers.

In reality, _most_ returns are typically by value. While it can make
sense to return a pointer, when you think of doing so, it's often worth
considering whether an iterator would produce a cleaner interface.
If you return an object allocated on the heap with the new operator,
the be clear who has ownership of the object--its class, the
recipient, or a third-party.

If you run into this much (outside of a few things like object
factories) chances are you should re-think your design from the
beginning.
Never return a reference to a class member allocated on the stack in
the header file. If your class replaces the value, then the user may
be left with an invalid reference, even though your object still
exists. Your class will never be able to remove the object as a
member. A user may manipulate the logic of your class in unexpected
ways.

If you have something "...allocated on the stack in the header file",
chances are pretty good that your header has some serious problems. A
header should normally contain declarations and class definitions, not
definitions of variables.
A method should modify an object constructed by the user by accepting
it as a non-const reference. Returning the same object would be
redundant and confusing.

Not necessarily. For example, nearly all streaming operators should
accept some sort of iostream object by reference, and return a reference
to that same object. This allows chaining the operators in the usually
expected manner.
How should members and their operations be handled in a general sense,
while considering effeciency and portability top priority?

The first advice I'd give is to NOT consider efficiency (at least as
most people use the word) as a top priority. Efficiency is rarely as
valuable as readability.

Using the same coding style regardless of what sort of object you're
dealing with will usually lead to problems in both efficiency _and_
readability though. Just for example, if you expect things to be small
and cheap to create and copy, you can far more reasonably deal with them
by value. If you expect them to be large and expensive to create and
copy, you often can't do that.

In a few cases, such as some template code, it's impossible to know what
type the code will be instantiated over -- but based on the things you
do with them, you can still often make a reasonable decision. For
example, there's no theoretical reason each 'character' in a string
couldn't be some large, complex, expensive type of object -- but given
what a string is generally used for, this would be fairly rare, so it's
reasonable to design a string class to expect that a single character is
small and cheap to create or copy.

In the reverse direction, if you're writing something like an FFT
routine, it's probably a lot better for it to assume that its operand
will be quite large and expensive to create, copy, etc. Yes, in theory
there's nothing stopping somebody from doing an FFT on an array of four
char's that might easily fit in a single machine register -- but it's
not a safe assumption in general.

Good design requires real thought, and nearly every rule will have
exceptions. Your priorities should usuallly run something like:

1) readability
2) readability
3) readability
....
98) readability
99) portability
100) efficiency

OTOH, on the rare ocassion that efficiency matters, it usually matters a
LOT. When it does, however, things like use of references vs. pointers
will rarely make enough difference to notice -- in most cases, you need
to think of things like changes in algorithms and/or simply eliminating
some operations entirely to make enough difference to care about.
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

On 2007-08-30 04:54, spekyuman wrote:

[snip]
Please, keep this constructive. :)

Please quote the relevant sections of the text you are replying to. In
general the only messages where you don't have to quote are those that
starts a new thread. Also, please don't quote signatures.
 
T

Tristan Wibberley

If an object is passed to a constructor or initialization method, the
user can expect the class to hang onto it. If a method saves an object
from an argument, choose an appropriate name like setColor(Color&) or
addInterpolator(Interpolator&).

I would have thought that const references made most sense here, taking
copies of the passed objects (by assigning the passed objects to its
members using operator=).
The worst excuse for using a pointer as an argument is that you want
to give it a default value of null. You still have to document what a
null object is supposed to mean. Worse, the user may overlook that the
argument exists or is optional. Declare a separate method that lacks
the extra argument. The effort is negligible.

QUESTION: Should all class members be implemented as pointers? Why or
why not?

Requiring several memory allocations when one would do and increasing
the risk of resource leakage? Not when it makes sense for the member to
copied when the owning class will be copied and the member to be altered
when the owner is altered.

QUESTION: Should all parameters be implemented as references? Why or
why not?

const references when you don't know the type (eg template stuff); by
value if it is known to be a built in type. I mostly use non-const
references when the function needs to modify the object (or keep the
reference for later modification) but won't own it and pointers when the
function *will* own it. It is a simple documentary technique that is
easy to be consistent with - reduces the risk of errors for other
developers - assuming that the convention is documented in my interface
documentation and exceptions are documented.

[Returning objects.]

One can always return objects from class methods by reference, either
const or non-const. A user can take the address of the reference, if
necessary, to save the object. But there are no drawbacks to returning
objects always as pointers. Consistency is preferable, and most API
return pointers.

No the consistency isn't in pointers vs references vs copies - it is in
relevancy. A resource allocation function would probably return a
pointer (or pointerish class!), a value (like Color) would probably be
returned by copy unless the object whose member function is returning it
has the colour as a member, in which case it would make sense to return
a const referece (that's a common accessor function idiom - and if you
need to return a smart reference to update reference counts or debugging
checks you can return a copy of the smart reference which itself holds a
reference to the value and has an implicit conversion operator to
actually do the copy if the client needs it).
Never return a reference to a class member allocated on the stack in
the header file. If your class replaces the value, then the user may
be left with an invalid reference, even though your object still
exists. Your class will never be able to remove the object as a
member. A user may manipulate the logic of your class in unexpected
ways.

No! If your class assigns to the member they will still have a reference
to the member. Only when you use pointers for all members do you risk
this problem.

QUESTION: "One can always return objects from class methods by
reference, either const or non-const. A user can take the address of
the reference, if necessary, to save the object. There are no
drawbacks to returning objects always as pointers." (Referring to the
statement that all objects should be implemented with pointer
members.) Are there any drawbacks when this is the case?

You can't *always* return objects by reference - only when you're not
making it look like a return by copy or when you've got the object
cached in some stable and documented way. For accessors I'd return a
reference, if I've got a copy that lasts for the life of the object I'd
normally return a const reference to it (and let the caller take a
copy), if I'm returning ownership of an object I'd do it by pointer (for
the documentary advantage) or by iterator if it's an iterator-creation
function, obviously.
How should members and their operations be handled in a general sense,
while considering effeciency and portability top priority?

Profiling and well documented assumptions (pre/post conditions).

With utility classes like smart pointers or raii wrappers it sometimes
also makes sense to look at assembler output just to be confident that
inlining works well for the cases your interface users will care about
the most.

--
Tristan Wibberley

Any opinion expressed is mine (or else I'm playing devils advocate for
the sake of a good argument). My employer had nothing to do with this
communication.
 
J

James Kanze

I found a public paper online stating, "The first time I returned to
writing C++ after a year of writing Java, I was appalled at how much
my design was constrained by managing the lifetime of objects. When C+
+ classes share objects, then they must negotiate who owns the object.
Garbage collection is not available, and smart pointers often fall
short." This is how the fellow introduced himself.

In sum, he starts by proving his imcompetence. For two reasons:
The first, of course, is that garbage collection is available
for C++, and is used in many C++ projects. The second is that
while garbage collection does relieve the programmer of a lot of
grunt work in managing memory, it doesn't do anything for the
lifetime of objects; managing the lifetime of objects is just as
important in Java as it is in C++. (Object lifetime, in fact,
is a design issue, independant of the language.)

He is correct in that smart pointers are a very poor garbage
collector.
He goes on to make
some design decisions for object management:
[No pointers as arguments.]
Pass all objects to class methods and constructors as references.
There is absolutely no advantage to passing objects as pointers. This
rule is euqally valid whether the objects are const or not.

One common coding guideline is to use pointers when you need a
"maybe" value (a null pointer), references otherwise. It's not
a universal coding guideline, however. I've seen at least three
different guidelines (all reasonable) concerning how to choose
between pointers and references: use references unless you need
a null value, use references unless there is a transfer of
ownership (e.g. delete), and use pointers anytime the lifetime
of the object must extend beyond the function call, or is
modified by the function call. I've also seen the guideline to
use pointers anytime the object might be modified, but there
seems to be a consensus that this isn't a good recommendation.

My recommendation is to choose one of the three rules, and use
it consistently. Except in cases where historical conventions
say otherwise: conventionally, streambuf is almost always passed
as a pointer, even in cases where it should be a reference
according to any of the above rules.
I recommend that all class members be saved as pointers.

Saved, in what sense?
You can easily take the address of an argument reference with
an ampersand and assign it to your member pointer. Some C++
programmers do not seem to realize that the address of a
references is the same as the address of the original object.
So they pass pointers when they want to save the argument, and
references when they do not. This is a poor form of
documentation, based on misunderstanding.

It's a question of convention, see above.

In general, I find that object members should usually be values.
Also, that value type objects shouldn't contain references, only
pointers (because otherwise, you have problems with operator=).
Finally, when an object contains a pointer, it is either the
owner of the pointed to object (i.e. it does both the new and
the delete), or the pointer is for navigation, in which case, it
usually can be null (which excludes passing a reference, even to
initialize it).
If an object is passed to a constructor or initialization method, the
user can expect the class to hang onto it.

Why? The usual C++ idiom is to copy.
If a method saves an object from an argument, choose an
appropriate name like setColor(Color&) or
addInterpolator(Interpolator&).
The worst excuse for using a pointer as an argument is that you want
to give it a default value of null. You still have to document what a
null object is supposed to mean. Worse, the user may overlook that the
argument exists or is optional. Declare a separate method that lacks
the extra argument. The effort is negligible.

The effort is small. But the gain is zero; in some cases, I
would even say it is a loss.
QUESTION: Should all class members be implemented as pointers? Why or
why not?

Certainly not. Class members of value types should be values.
QUESTION: Should all parameters be implemented as references? Why or
why not?
[Returning objects.]
One can always return objects from class methods by reference,
either const or non-const.

Only if the class actually contains the object, and even then,
you end up with a dependency on the lifetime of the containing
object. Most of the type, member functions should return
values.
A user can take the address of the reference, if necessary, to
save the object. But there are no drawbacks to returning
objects always as pointers. Consistency is preferable, and
most API return pointers.
If you return an object allocated on the heap with the new operator,
then be clear who has ownership of the object--its class, the
recipient, or a third-party.

One classical convention for "being clear" about this is to
return an std::auto_ptr if the function is renouncing ownership.
Think about whether you are breaking encapsulation of member
data in a way that will prevent modification later.
Never return a reference to a class member allocated on the stack in
the header file. If your class replaces the value, then the user may
be left with an invalid reference, even though your object still
exists. Your class will never be able to remove the object as a
member. A user may manipulate the logic of your class in unexpected
ways.

I don't understand the above. How do "header files" come into
this?
A method should modify an object constructed by the user by
accepting it as a non-const reference. Returning the same
object would be redundant and confusing.

In general, arguments fall into three types:

in: Prefer pass by value in C++. For optimization reasons,
it may be preferrable to use pass by const reference,
and there is one case (the copy constructor) where it is
required, and another (the assignment operator) where it
is conventional.

inout: Non-const reference in C++.

out: Prefer a return value. If there is more than one out
parameter, however, all but one must be non-const
references, and if the object is expensive to copy,
optimization considerations may require using a
non-const reference. Note, however, that regardless of
the design intent, a non-const reference is an inout
parameter---there is no way in C++ to pass a non-const
reference to uninitialized memory, where the function
will construct a new value.

In general, if the function is declared to take a value or a
const reference, you know that it is an in argument; if it is
declared to take a non-const reference, you should suppose
inout, and the function should document the significance of the
in value. (If the parameter is conceptually an out argument,
then the function should document that the in value is
irrelevant.)

In general, the return type should be a value, unless it
explicitly must designate an existing object. Optimization
considerations may occasionally lead to returning a const
reference instead of a value, but this can only be done if there
is an existing instance of the object outside of the function;
threading may restrict even this. If the return value is
guaranteed, then it should be a value or a reference; if it is
not guaranteed, then it should be a Fallible or a pointer.
QUESTION: "One can always return objects from class methods by
reference, either const or non-const. A user can take the address of
the reference, if necessary, to save the object. There are no
drawbacks to returning objects always as pointers." (Referring to the
statement that all objects should be implemented with pointer
members.) Are there any drawbacks when this is the case?

The first sentence above is false. You can only return an
object that exists elsewhere by reference. You cannot return a
synthesized value by reference, ever. You can also get into
trouble returning references in a multithreaded environment.
How should members and their operations be handled in a
general sense, while considering effeciency and portability
top priority?

By value.
 
J

James Kanze

On 2007-08-29 20:57, spekyuman wrote:
The question about who owns an object have nothing to do with pointers
references or such. It's a problem you can run into in Java too, you
just don't have to deal with deleting pointers.

Yes and no. In C++, the delete operator does two things: it
logically ends the object lifetime (disposes of the object),
*and* it frees memory. In Java, these two are always distinct,
but if an object needs explicit disposal, then you still have to
do it manually. (Note that some low level idioms in C++ require
separating the two as well; I've never seen a container class,
pre-STL or post-, which didn't separate them, for example.)

[...]
If you are not aware of the differences between normal members, pointers
and references then it's time to start studying. My personal preference
is to avoid pointers if possible since there are certain problems only
pointers can create. That does not mean that I don't use them however.

It depends on context. I avoid references in object types with
value semantics, because of the problems they cause with
assignment. And pointers are, of course, a no-no where operator
overloading is involved. Beyond that, it's largely a question
of local conventions.
 
J

James Kanze

[...]
Observation #1 Implementing all member objects using pointers and
references, in large systems, will impact performance.

Not just. If an object has value semantics, then passing it by
reference leads to all sorts of problems, independantly of
performance. See my earlier posting, concerning in, inout and
out arguments.

Note that all languages support value objects in some way. They
must, since value semantics are part of the design space. In
C++, value semantics are the default, and user defined types
should either support them (by ensuring a semantically correct
copy constructor and assignment operator), or turn them off (by
declaring the copy constructor and assignment operator private,
or by inheriting from something like boost::uncopiable). In
Java, reference semantics are the default, and value semantics
must be simulated by declaring the type final, and ensuring that
it is unmutable. (See java.lang.String for an example of this.
Or java.awt.Dimension for the problems which result when it
isn't done.)

Deciding which types are values, and which belong to some other
category ("entity" objects, plus other special types) is a
design decision, which heavily affects how you implement the
object (regardless of the language).
 
J

James Kanze

[ ... ]
This is lunacy. In reality, rather the opposite is true: a class should
contain at most one member that's a pointer, and when it does, the class
should be dedicated to managing that pointer. If it's not dedicated to
managing a pointer, a class shouldn't contain a pointer -- it should (at
most) contain instances of other classes that manage pointers on its
behalf.

Come now Jerry, you can't really mean this. Are you saying that
nodes in a red-black tree should only contain smart pointers (a
class dedicated to managing a pointer)? And what about pointers
used for navigation between entity objects? The object
containing the pointer must be informed about "lifetime" events
concerning the pointed to object, but encapsulating the pointer
into a separate class isn't necessarily (or even usually, in my
experience) the best solution.

I think the important points here are:

-- C++ supports value semantics quite well, thank you. We
don't have to (and usually shouldn't) play around with
pointers and references when we're dealing with values.

-- Pointers and references are language level tools, which can
be used to implement a number of high level concepts. Once
the design is done, and we know what we are trying to
achieve, we can choose which ever tool is most appropriate.
Raw pointers work very well for navigation, for example.

Your rule really applies mainly to cases where you are
allocating objects dynamically in the constructor. In such
cases, you definitly want some sort of wrapper for the pointers,
so that they will be correctly destructed if the constructor
fails (an exception) after construction of one or more of the
objects is completed.
Of course, quite a few things simply don't need to be pointers
at all -- directly, or managed by another class. Storing a
pointer where you really only need a char would be incredibly
foolish and wasteful.

I'd argue that the same thing holds for strings or other types
with value semantics: std::string const* are very, very rare in
my code. (Off hand, I think they only occur as return values,
when the function does some kind of look-up which may fail.)
[ ... ]
The worst excuse for using a pointer as an argument is that you want
to give it a default value of null. You still have to document what a
null object is supposed to mean. Worse, the user may overlook that the
argument exists or is optional. Declare a separate method that lacks
the extra argument. The effort is negligible.
The effort may be negligible, but appears to accomplish nothing -- using
overloading instead of a default argument makes no real difference in
the documentation required.

You may or may not want the default argument; there's nothing
wrong with requiring the user to specify explicitly that the
argument is not provided. More to the point, documenting two
different functions is more work than documenting just one.
More work for the person documenting, and more work for the
person having to read and understand the documentation. If the
semantics of the function really are different in the absense of
the argument, you need to document both semantics, the
difference is negligeable, and using the same function for two
different semantics is confusing. If the semantics are more or
less the same---say, the function uses a reasonable default when
the argument isn't provided---then it's much easier for the user
to get his head around one function than around two.
Either way you need to document the number and types of
parameters allowed and what is accomplished with different
numbers of parameters.

[...]
The first advice I'd give is to NOT consider efficiency (at least as
most people use the word) as a top priority. Efficiency is rarely as
valuable as readability.

Note that the real problem isn't the word "efficiency"; it's
what the word is applied to. Increasing programmer efficiency
should be a top consideration.
Using the same coding style regardless of what sort of object you're
dealing with will usually lead to problems in both efficiency _and_
readability though. Just for example, if you expect things to be small
and cheap to create and copy, you can far more reasonably deal with them
by value. If you expect them to be large and expensive to create and
copy, you often can't do that.

Actually, you can do it more often than you would think. It
only becomes a problem if the function is called very
frequently, or in a critical path or loop. If a function has a
single out argument, I will return it by value, regardless of
the type, until the profiler tells me I can't. (I've
successfully returned my pre-STL equivalent of an std::list with
60000 elements, using a compiler which didn't implement RVO, and
without "performance problems". The return statement, on the
machines of that epoch, took some seven seconds, but the
function was only called five or six times in an application
which otherwise required a couple of hours runtime.)

Of course, if it had been a question of a function argument,
rather than a return value, I would have used a const reference
rather than a value; it's pretty automatic to pass by const
reference whenever a container is involved---for most people,
it's pretty automatic as soon as a class type is involved
(although the STL uses pass by value a lot for class types:
iterators, predicates, etc.).
 
P

Pete Becker

Note that the real problem isn't the word "efficiency"; it's
what the word is applied to. Increasing programmer efficiency
should be a top consideration.

In almost all contexts. But if I'm selling a commercial library, I have
to pay a great deal of attention to the speed and memory usage of the
code, because customers won't want to or perhaps won't be able to fix
performance bottlenecks in my code. Your later example of a function
that took seven seconds to copy its return value is a good one: you
knew that it was only called a few times in several hours, but the
writer of a general-purpose library doesn't have that information and
probably can't impose that constraint on customers.
 
J

Jerry Coffin

[ ... ]
Come now Jerry, you can't really mean this. Are you saying that
nodes in a red-black tree should only contain smart pointers (a
class dedicated to managing a pointer)?

Well, yes, it was something of an exaggeration. It's perfectly
reasonable for a binary tree node or linked list node to contain to
contain two pointers. Likewise, a B-tree node can reasonably contain a
whole array (or vector, etc.) of pointers. These are, however, fairly
special cases, and (particularly) the pointers involved are basically
just more of the same kind.
Your rule really applies mainly to cases where you are
allocating objects dynamically in the constructor. In such
cases, you definitly want some sort of wrapper for the pointers,
so that they will be correctly destructed if the constructor
fails (an exception) after construction of one or more of the
objects is completed.

Yes -- I was thinking specifically of the kind of case where you had a
class that would normally contain values, and you replaced all of them
with pointers, and had to allocate an actual object to back each pointer
in the ctor, destroy them all the dtor, manually track exceptions to
ensure against leakage if one of your allocations failed, etc., ad
nauseam.
I'd argue that the same thing holds for strings or other types
with value semantics: std::string const* are very, very rare in
my code. (Off hand, I think they only occur as return values,
when the function does some kind of look-up which may fail.)

Oh certainly -- I was only giving one example among _many_.
You may or may not want the default argument; there's nothing
wrong with requiring the user to specify explicitly that the
argument is not provided.

True -- but the original comparison was specifically between using a
default argument or overloading the function to provide one for each
number of arguments you decided to support. While the latter is
certainly useful at times, I don't see it as being strongly preferable
as a general rule, especially if the overloads do exactly the same
thing, but with some default value(s) assigned to the parameters that
haven't been supplied.

[ ... ]
Note that the real problem isn't the word "efficiency"; it's
what the word is applied to. Increasing programmer efficiency
should be a top consideration.

Quite true -- but based on the OP, I interpreted efficiency as relating
primarily (if not exclusively) to run-time speed, and possibly
minimizing memory usage.

[ more elided to which I had nothing to add...]
 
W

werasm

James Kanze wrote:
I recommend that all class members be saved as pointers.
Saved, in what sense?

I think he means stored as a pointer, as opposed to
stored as a reference, in the case where it is
initialized from an external reference during
construction:

struct X
{
X( int& v ): v_( &v ){}
int* v_;
}; //as oposed to
struct X
{
//...
int& v_;
};

I can't see why he would recommend this, though.
There there are many other factors to consider when
deciding how the member is stored, which
I realize you know and there is no explicit need
for me to mention these.

Perhaps I could mention that if one knows that
the constructor parameter's lifetime always exceeds
that of the member, and if one knows that null
values are not an option, then storing as reference
is not bad at all. For me this conveys intent.

Regards,

Werner
 
T

Tristan Wibberley

Perhaps I could mention that if one knows that
the constructor parameter's lifetime always exceeds
that of the member, and if one knows that null
values are not an option, then storing as reference
is not bad at all. For me this conveys intent.

Indeed, it's frequently the best choice - for example, in adaptor
classes where I want the compiler to stop me accidentally changing what
the instance is adapting.

--
Tristan Wibberley

Any opinion expressed is mine (or else I'm playing devils advocate for
the sake of a good argument). My employer had nothing to do with this
communication.
 
J

James Kanze

James Kanze wrote:

[...]
Perhaps I could mention that if one knows that
the constructor parameter's lifetime always exceeds
that of the member, and if one knows that null
values are not an option, then storing as reference
is not bad at all. For me this conveys intent.

It depends. If the class has value semantics, and the address
of the object is part of it's value, then references won't work.
I find that I have a lot of smaller, helper classes whose
constructor takes a reference (since I don't allow null), but
which store the address as a pointer, since it must be changed
in the assignment operator.

If the class doesn't support assignment, then I would agree.
 
W

werasm

James said:
It depends. If the class has value semantics, and the address
of the object is part of it's value, then references won't work.
I find that I have a lot of smaller, helper classes whose
constructor takes a reference (since I don't allow null), but
which store the address as a pointer, since it must be changed
in the assignment operator.

Agreed, hence my mentioning there are many factors that
contribute. I've even contemplated this idea:

struct requires_assignment
{
requires_assignment(): ref_( value_ ){ };
private:
static const int value_;
const int& ref_;
};
const int requires_assignment::value_( 0 );

struct Derived : requires_assignment{
//Derived& operator=( const Derived& )
//{ return *this; }
};

int main()
{
Derived d1, d2;
d1 = d2; //Remove to compile, or
// define op=
return 0;
}

I have no use for it at this moment though.

All said, I find that I'm beginning to store references less
often, and have started using weak pointers or shared
pointers (it seems safer). I only store a reference when
I'm really sure. For instance if a nested class requires
a reference to its parent, and the parent is the owner
(given the nested does not require value semantics,
yes, which in my case I've found to be rare). Admittedly
I've not had the time to look into GC yet (from other posts
I gather you would consider this incompetent), and I know
that in some cases knowing when the lifetime of something
ends is Grey, for example:

I (or we) often use the Command pattern to communicate
between threads, usually wrapping member functions, and
in that case one wants the reference (or pointer to receiver)
to outlast the call, or one wants to at least see when the
receiver dies. Shared pointers are an option, but I don't trust
them entirely due to the fact that for this scenario simultaneous
read/writes are required to the shared pointer. OTOH
encapsulating calling into objects from other threads in this
way (using commands) are very beneficial to us because
it reduces data sharing (apart from the encapsulated pointer
to receiver) and allows for objects to be bound to threads,
promoting a kind of concurrent sequential style of
programming.

Regards,

Werner
 
C

Chris Thomasson

[...]
Shared pointers are an option, but I don't trust
them entirely due to the fact that for this scenario simultaneous
read/writes are required to the shared pointer.
[...]

Simultaneous reads/writes are compatible with smart pointers that can
provide the 'Strong Thread-Safety guarantee:

http://groups.google.com/group/comp.programming.threads/browse_frm/thread/e5167941d32340c6

Boost's shared_ptr only provides normal/basic thread-safety, so its not
going to work in your scenario. If your interested in taking a look at the
source-code for some atomic smart pointers that do exhibit strong
thread-safety:

http://appcore.home.comcast.net/vzoom/refcount

http://atomic-ptr-plus.sourceforge.net



Here is some more information on my "mostly" lock-free atomic refcount
algorithm:

http://search.gmane.org/?query=&group=gmane.comp.lib.boost.devel&author=chris thomasson
 

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,774
Messages
2,569,596
Members
45,142
Latest member
arinsharma
Top