Jump to content
  • Advertisement
Sign in to follow this  
My_Mind_Is_Going

Using references for 'out parameters' in C++

This topic is 3435 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Suppose you're writing some sort of collection class (holding objects of type T) that has an iteration member function called GetNext, which is supposed to return each successive item in the collection until it reaches the end. What's a good signature for this function to take? Returning by reference or by value: T GetNext() or T &GetNext() don't seem appropriate because if you're at the end of the collection there's no obvious way to signal this in the return value -- you can't return a null reference in C++. Using an out reference parameter with a return value to signal success or failure: bool GetNext(T &next) Is better because you can return false if there wasn't a next item to return. The problem here is that it requires the caller to have constructed a dummy T object in order to have a reference to pass in the first place. Using a pointer here seems to be the only reasonable way to do this -- something like: T *GetNext() or bool GetNext( T **next ) Where a null pointer can be return to signal failure in the first case and nobody is required to instantiate a T object in the second case. Does anyone have any other insight on this? I'm being asked to write something like this for a class and the instructor has specified the signature as: GetNext(T &next). I'm sure for textbook examples where T is an int or char this doesn't matter, but if T were a complicated type this just seems awkward to me.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
I'm being asked to write something like this for a class and the instructor has specified the signature as: GetNext(T &next). I'm sure for textbook examples where T is an int or char this doesn't matter, but if T were a complicated type this just seems awkward to me.
It is awkward, and the fact that your instructor is making you embed iteration state into your container does not speak well for him. When in Rome, though. Do it his way, and next time don't do it that way. The proper way to implement iteration is with an iterator class.

Share this post


Link to post
Share on other sites
If an instructor asked you to do something, then that's what you do. There's no sense in risking points on your assignment because you felt a particular requirement was awkward or weird. At the very least, talk to your instructor and see if he's willing to accept other signatures. If he is then great, otherwise you're stuck.

Assignments aside, you could follow SC++L's approach and create a simple iterator object for the collection.

EDIT: Effing ninjas! :)

Share this post


Link to post
Share on other sites
The best answer in this case is to use exceptions. If at the end of the collection, throw an exception. Exceptions have overhead when thrown and caught, but not otherwise. Add a hasNext() method. The correct usage of this pair of methods ensures that no exceptions are ever thrown:

while(there are more elements) {
...
get that element
...
}
Taking compiler optimization into account, and inlining the hasNext() method, this is probably the best option possible for a generic container if you have to keep the iterator stage stored in the container (which as mentioned above is usually a bad idea).

The exception throwing is just there for conformance with the specification that a reference is returned and to make stuff compile.

Share this post


Link to post
Share on other sites
You can also throw an exception when there is no next. (This, I believe, is how Python terminates all iterations, although in C++ mindset exceptions are for unexpected error conditions.)

Also, what your instructor is asking is a bit like how the streams work. They also take the out parameter by reference and if the input fails they don't change the out parameter and set a fail bit which you can then check.

For example, a loop that reads input as long as there is any:

//istream& operator>>(istream& is, int& n);

int n;
while (cin >> n) {
//...
}


The more C++ way for containers is an iterator class as already said. Also it would be better if the start iterator was returned from a method called begin() (and not GetFirst, StartIterator or whatever) etc. The usability of the class in templated code depends on classes following a similar interface. E.g, if your method to add an item to the back of the container is called push_back(), and not Add, PushBack, Append ..., it might happen that std::back_inserter(MyContainerInstance) automatically works. That is, you'll get additional tools for your class for free.

Share this post


Link to post
Share on other sites
It sounds like youre a little unclear on the spec for the assignment, but given the current information, I would do something like:
template <class T> class List
{
private:
Node* first;
Node* cursor;

public:
//standard linked list stuff

//advances the cursor to the next node and returns the data
T& GetNext()
{
cursor = cursor->next;
return cursor->data;
}
};

Storing an iterator inside the list is usually considered bad form, because it's incompatible with multi-threaded applications. However, I doubt that matters for the task at hand.

There are a couple ways you could handle the end of the list thing. You could make it a cyclic list, such that the last node->next = first. You could also change the function to return a pointer, so that it could return NULL when cursor == NULL.



Share this post


Link to post
Share on other sites
Quote:
Original post by My_Mind_Is_Going
I'm being asked to write something like this for a class and the instructor has specified the signature as: GetNext(T &next).
If that is what your instructor has specified then use it, no matter how bad an idea it is. In this situation exceptions are the correct way to go. In order to know when to stop calling the function, so to avoid the exception, you would need a HasNext predicate function, or you could rely on the Size function.

Share this post


Link to post
Share on other sites
The standard iterator concepts are a fairly good design.
The only thing that would make them better would be allowing the end iterator to be of a different type.

Share this post


Link to post
Share on other sites
Quote:
Original post by My_Mind_Is_Going
I'm being asked to write something like this for a class and the instructor has specified the signature as: GetNext(T &next). I'm sure for textbook examples where T is an int or char this doesn't matter, but if T were a complicated type this just seems awkward to me.


Back in the day the C language did not have references. All parameters had to be passed by value, so the only way to implement an 'out' parameter was to take the address of a local variable and pass it to your function, and it would have it's value set by that function through the miracle of pointer dereferencing. A C-with-classes programmer would just substitute the use of pass-pointer-by-value with pass-by-reference.

Earlier than that, FORTRAN programmers got in the habit of using parameters for calculation inputs and results (FORTRAN passed by descriptor, which are effectively like references for the purpose of this discussion) and would indicate the status of an operation by returning a status value. C programmers adopted this idiom, except where they didn't.

It sounds like your instructor is a C programmer trying to implement the Java cursor idiom in C++. Were I to do that (it's surprising what you're willing to do to put bread on the table), I would implement the GetNext function like so.
bool GetNext(T& next);
although true C-with-classes would have this signature
int GetNext(T &next);
Either way, the value of next would only be valid if the GetNext function returned a value that was convertible to true. In other words, when calling GetNext on a collection in which the current cursor state is at the last element, just return false/zero.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!