[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?
> How should members and their operations be handled in a general sense,
> while considering effeciency and portability top priority?
>
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)
--
Bob R
POVrookie
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?
> > How should members and their operations be handled in a general sense,
> > while considering effeciency and portability top priority?
Profile!
Provide some context for your questions. 'in general' gets an 'in general'
answer (like I suggested).
> > QUESTION: Should all class members be implemented as pointers?
> > Why or why not?
No. Because it isn't required in all cases. (POD struct/class).
> > QUESTION: Should all parameters be implemented as references?
> > Why or why not?
No. Because it isn't required in all cases. (POD struct/class).
> > 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?
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.
--
Bob R
POVrookie
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.
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.
--
Erik Wikström
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. :)
[ ... ]
> [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.
--
Later,
Jerry.
The universe is a figment of its own imagination.
[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.
--
Erik Wikström
> 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.
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.
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> > 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.
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.
[...]
> 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).
> [ ... ]
> 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.
[...]
> > 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.
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.).
[ ... ]
> 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.
> > 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.)
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.
[ ... ]
> > 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.
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...]
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
> 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.
[...]
> 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.
> 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
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%20thomasson
Here is some more information on Joe Seighs atomic_ptr:
http://groups.google.com/group/comp.programming.threads/browse_frm/thread/3c763698da537d8f
http://groups.google.com/group/comp.programming.threads/browse_frm/thread/f2c94118046142e8
> Simultaneous reads/writes are compatible with smart pointers that can
> provide the 'Strong Thread-Safety guarantee:
Yes, but boost's promises concerning this is not good enough (with all
respect).
I find that in an MT environment, one most often requires the strong
guarantee, especially for shared pointers. I can (and have) thought
of
many cases (if not most of the cases I encounter) where simultaneous
read/writes are probable, and boosts shared pointers do not promise
much.
I use boosts shared pointers mostly for their custom deleters, and
only in
one thread.
>
> http://groups.google.com/group/comp.programming.threads/browse_frm/thread/e5167941d32340c6
>
Yes, I've followed this thread for a while, but should look again,
Although
I wish the effort would be standardized for all platforms.
> 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:
Yes, so I gathered from the documentation. I have considered in the
past to comment on that in this group (the fact that I feel they
guarantee
too little, but eventually I did not for various reasons).
Thank you for the response,
Werner