Letters to the editor may be sent via email to cujed@mfi.com, or via the postal service to Letters to the Editor, C/C++ Users Journal, 1601 W. 23rd St., Ste 200, Lawrence, KS 66046-2700.
Dear CUJ
A co-worker and I are have a disagreement over the use of a single instance class.
I have a number of situations where I want the information in a class to be available to a number of other classes. For instance, I have a time class that, based on a start time, records and tracks time-based events, converts local time to a J2000 reference, etc. This information is used by a number of other classes and most elements never change after instantiation.
I came across the following method to ensure a single instance, yet make the class accessors available process-wide:
UtConvertTime.h class UtConvertTime { public: static UtConvertTime *UtTimeInstance(); private: // constructor UtConvertTime(); // the address of the only // instance of this class static UtConvertTime *itsInstance; }; UtConvertTime.cc UtConvertTime::UtTimeInstance(void) { if ( itsInstance == 0 ) itsInstance = new UtConvertTime(); return itsInstance; }It is accessed this way:
#include "UtConvertTime.h" void foo( void ) { // The first time this is called // from anywhere, it instantiates // the class. Every subsequent // call returns a pointer to the // original. UtConvertTime *ptrToTime = UtConvertTime::UtTimeInstance(); }I think this is an elegant way to handle the problem and remain within the C++/object-oriented philosophy.
My co-worker disagrees but her only solution is to create a global pointer to the class and instantiate it in the start-up sequence.
What are your thoughts and is there a better way?
Thanks, JP
We posed your question to our Consulting Editor, Chuck Allison. He said:
"A Singleton Pattern [as implemented in your single-instance class] is the clear solution. The other solution besides a Singleton, of course, is to have a class with only static information, or somewhat equivalently (notwithstanding Dan Saks' recent column), put things in a namespace. This would be closer to (but not as careless as) the co-worker's solution. One needs a little more context to decide between the two. Both solutions have their place."
Dear Mr. Briand,
In your followup to David Tribble's letter in the mail section of the June 1998 C/C++ User's Journal, you mentioned that you wanted to hear reasons for using references as arguments to functions, rather than pointers. This argument (that word again!) has bounced around in C++ circles for quite some time now, but you asked, so I figured I'd "pile on."
I actually found Mr. Tribble's remark that he prefers pointer arguments to reference arguments quite breathtaking. One could make a case for never using a pointer argument, except to interface to routines written in other programming languages.
Pointers are semantically different from references. Mr. Tribble stated it himself: You can change a pointer so that it points to a different object. You can't change a reference. This is a Good Thing.
An added complication is that with a pointer argument, someone can pass a NULL pointer in as an argument. A function that takes a pointer argument must either assume (or hope) that no caller ever passes in a NULL, or the function must explicitly check to see if the value is non-NULL every time it is called. This may slow down a program if it happens often enough.
I suppose the guideline is this:
If the argument is supposed to refer to a single object throughout the lifetime of the function, use a reference (or pass-by-value for a builtin type that won't change). I suspect this will be the case most of the time.
If there is some well-defined meaning for "no object at all" being passed to the function, or for changing the pointer inside the function to point to a different object, then by all means use a pointer (well, no, don't use a pointer, but that's a different story).
Best regards,
Spencer Simpson
Dear CUJ,
Mr. Briand suggests that supporters of reference arguments speak up. I'd hate to see my side go down without a fight, so here I am. I hope this will make it clear why I intend to continue to use reference arguments despite the contrary arguments raised in your pages.
I see three places where the issue is important: in a function's body, in a function's argument list, and where the function is called. In a function body, the reference syntax is clean and elegant. The argument list tells you the variable may change; you change it using the standard syntax. If the variable names are the same, you can use the same code in the function and the place where the function is called. It's nice and consistent. This is a minor win for reference arguments.
Next, consider f(int *foo). What exactly is foo? It could be a pointer to a single integer that we wish to modify, or it could be a pointer to an array of integers. By contrast, f(int &foo) is unambiguous. We have a single integer, and we wish to be able to change it. (It might be argued that it is more appropriate to say f(int foo[]) when you have an array, but the pointer usage is very well established consider the standard use of char *s as strings, for instance.)
The situation gets even more complex with class variables. Might (MyClass *foo) delete the pointer you pass to it? This form is triply ambiguous. It could be a pointer to a class object; it could be an array of class objects; or it could have the more specialized meaning of a class object that lives on the heap. While in theory could take the address of foo in f(MyClass &foo) and play around with it like it was on the heap, it is highly unintuitive, and I think most programmers would reasonably expect f not to do so.
So in the function's argument list, reference arguments are more descriptive and precise than pointer arguments. This is a huge advantage.
Which brings us to the point where the function is called. It is quite true that the address-of operator makes a nice marker that we expect the function to change the value of the argument. The problem is that this signal is not reliable. Suppose we eliminated call-by-reference. Consider
f(foo); g(&bar);What can we say from looking at this? The value of foo is not changed; the value of bar might be. However, this uses "value" is in its most limited sense. If foo is a pointer, the value we really care about is probably the object it points to, not that pointer. From this function call, we know nothing about whether that object is changed. To determine that, you need to look at the functions and their argument lists:
f(MyClass *foo); g(MyClass const *bar);Now we see that the object which we are really interested in is likely changed in f, but guaranteed unchanged in g. So much for the address-of operator providing us much useful information at the point of the call.
If there are any pointers involved, you simply can't tell what will happen in the function call at the point of the call. You need to see the function's argument list and definition. These are both made clearer by reference arguments. For this reason, I prefer using call-by-reference.
This is not a call to eliminate pointers. They (or some abstraction to encapsulate them) are fairly essential when dealing with objects on the heap. In that case, pointer arguments are quite reasonable. The difference is that with heap objects, the pointer value is important in and of itself. With stack objects, the pointer is completely incidental.
The actual system I support looks something like this:
f(int foo), f(MyClass const &foo)The function needs to see foo, not to change it. (With small classes, I may use call-by-value.)
f(int &foo), f(MyClass &foo)The function needs to be able to change foo.
f(MyClass *foo)Here MyClass would be one whose objects live on the heap.
f(MyClass const *foo)The same as the previous, except that foo will not be modified. Alas, I don't use this consistently yet. Old habits die hard.
f(auto_ptr<MyClass> foo)Not only do MyClass objects live on the heap, this function takes ownership of this particular object. It will ensure it gets deleted properly.
template <class It> f(It first, It last)This is my new theory on the proper way to receive an array. I haven't really tested this much yet...I don't know how to distinguish const from non-const arrays yet.
The last two are pretty speculative; I haven't had my hands on a compiler close enough to the Standard to really use them until the last few months. The first three are long-established habits I never really thought about before. Thank you for making me justify myself!
Sol Foster
colomon@alf.org
CUJ,
Just out of curiosity, is there any performance difference in using a reference instead of a pointer? I am assuming that what is actually being done in the generated code in both cases is to load a pointer into a register somewhere without actually copying any data except the pointer.
Scott VanSickle
scottvs@timbrelmedical.com
Timbrel Medical Development Corp
Glen Rock, NJI posed your question in the following form to columnist Pete Becker:
"Is there any reason to expect a performance difference in using a reference argument vs. a pointer? For example:
void foo1(SomeClass &a) { int bar = a.GetBar(); // do something with bar a.PutBar(bar); } void foo2(SomeClass *a) { int bar = a->GetBar(); // do someting with bar a->PutBar(bar); }Is there any reason to expect one of these functions would be faster?"
Pete replied:
"The most natural way to implement references is with pointers. Every implementation that I know of does that. In that sense, a reference is just a pointer that's automatically dereferenced. These two code samples will generate exactly the same code. Pete"
Beyond this question, the debate appears to (mostly) boil down to whether it is more important to protect the callee from bad arguments (such as null pointers), or to alert human readers of code to function calls that can result in changes in the caller. It would be interesting to find out if library writers were mainly advocates of the former and library users advocates of the latter. But I'll let someone else research it. I believe we've puréed this horse. mb
Dear CUJ,
I found Mr. Marcok's article an excellent use of templates, combining elegance with efficiency. However, the construction using expression templates seemed to me over-engineered a bit here. Trying to get rid of C++'s lack of parametrizing the number of parameters for a function, he reaches a solution that while safe and efficient I don't think is elegant and simple enough to justify its implementation complexity. (Actually, for a template novice, it's much easier to understand the implementation of the entire array than its constructor.)
Instead, I propose a solution that is just as fast, admittedly not that rock-solid, but much more simple to use and learn: pass the arguments as a variable list of integers and finish that variable list with -1 (see Figure 1). Although the -1 is not required in theory, it provides a redundancy that is excellent for validating that the user has passed the correct number of parameters.
Another small correction is to provide for initialization (calls to init()) in increasing order, just like the construction order. (The best would be if Mr. Marcok used an initialization mechanism like in STL, so the init method could be replaced by a constructor.)
The usage example would look simpler:
void main() { size_t a, b, c; cin >>a >> b >> c; Array<int, 3> A1(a, b, c, -1); // ... }Yours,
Andrei Alexandrescu
Zlatko Marcok replies:
Dear Andrei
Thank you very much for your comments. I fully agree with you that the expression template method is over-engineered (like breaking an egg with a hammer). It falls into the category of solutions looking for problems and I am a little embarrassed that I could not come up with a more worthy problem to solve.
My intent with the article was to present two very interesting language features. Even if nobody uses them for arrays, the article will serve a good purpose if it triggers ideas for solutions to other problems. The array problem is simple enough so that it does not interfere explaining the template techniques. (Please note that by "presenting" the techniques, I do not intend to say that I invented them.)
Your technique is also appropriate, and is hinted at by Listing 3 in my article. I like the use of the variable argument list. I have become so used to the signature matching in C++, that I did not even think of using varargs.
I don't think the order of calls to init has any practical consequence, but I agree with the spirit of your comment. Code that follows well-known conventions is generally better.
Thanks for your time in mailing me, and especially in reading the article.
Best regards
Zlatko Marcok
Correction
In the August 1998 issue of CUJ we published the wrong bio for John T. Bell, author of the article "A Wrapper Class for NT Services." The correct bio is as follows:
John T. Bell is a Principle Software Engineer for RWD Technologies in Columbia, MD. He has been developing software professionally since 1979. He has a Masters degree in Computer Systems Management and a Bachelors Degree in Electrical Engineering, both from the University of Maryland. He can be reached at jbell@rwd.com.
We offer our apologies to John Bell and to any readers who may have been inconvenienced by this error. mb