the c# return statement

C

cody

I didn't know you were looking for a code review for this code fragment.
Which is why as part of the contract of the method, you specify that NULL
indicates an invalid type was requested.


Which, if unknown types are found in the serialized stream, is indeed an
exceptional case.


Ahhh... fail silently... ok. That sounds like a wonderful idea.


Hey guys, did you ever considered that which method to use heavily depends
on the application/the circumstances?

If the argument was invalid and I know that my platform supports exceptions
and an invalid argument is a real exceptional circumstance I throw an
exception.

If the argument is expected to be invalid sometimes, I return NULL (for
example a method which returns an object found in a hashtable, if not found
return NULL).

The proposal with the nullObject might also be a good idea under certain
circumstances, for example a method which returns a description of something
depending on the parameter, and if not found I could reuturn an empty string
(the null-object in this case), this way the client doesn't has neither to
check for nullity nor catch an exception, it simply displays the string.
This was just a bad example but there are really rarely cases where this
approach might beuseful.
 
C

cody

Yet another time where you misread Factory patterns for State patterns.
Please show the exact example I gave - that you said returning references
would be suitable for.

In case you've forgotten, this means that you must return a reference from
a function which looks like this:

Object* CreateSomething()
{
return new Object();
}

So go ahead, Ian. Rewrite that function to use a reference as you claimed
could be done. I'm waiting.

Where is the problem?

Object & CreateSomething()
{
return *new Object();
}

The only problem is that returning references says "the return value should
not be null", as nobody would expect a reference to be null.
In this case the method should throw an exception if no suitable object can
be created.
 
M

Milo T.

Clearly you still don't understand or you are trying to be obtuse.

1) I stated that it is necessary to understand what is happening. If the
parent has state which it needs to clean up, then the destructor cannot be
virtual as the destructor of the parent class will not be called if
operating on a child class. I guess that you only use parent classes with
no state or you let them leak.

That comes under section (4) - you don't understand how virtual destructors
work. Perhaps you need remedial C++ training.

So here's some remedial C++ training for you.

class Object {
public:
virtual ~Object()
{
printf("Object Destructor called\n");
};
}

class Table : public Object
{
public:
virtual ~Table()
{
printf("Table Destructor called\n");
};
}

class IKEAGlassTop : public Table
{
char* pMemory;

public:
IKEAGlassTop()
{
pMemory = new char[1000];
}

virtual ~IKEAGlassTop()
{
printf("IKEAGlassTop Destructor called\n");
delete pMemory;
};
}

So I create a new IKEAGlassTop table, and pass it to something else which
understands Table objects. What happens when the destructor on my Table is
now called?

Answer:
If I have a virtual destructor, there is a vtable lookup, which locates the
IKEAGlassTop destructor, which is then called, deleting the memory
allocated when IKEAGlassTop was created. Then, the Tablet destructor is
called, followed by the Object destructor. You get the following output:

IKEAGlassTop Destructor called
Table Destructor called
Object Destructor called

Now... what happens if you do the same without a virtual destructor?

The Tablet destructor is called, which then calls the Object destructor.
The memory allocated in IKEAGlassTop's constructor is not free'd, and a
memory leak ensues.

THIS IS THE EXACT SCENARIO YOU CLAIM HAPPENS *IF YOU HAVE VIRTUAL
DESTRUCTORS*.

Your output on the screen will be this:

Table Destructor called
Object Destructor called

.... and if you don't believe me, then you might want to try running the
code for yourself.

Face it, Ian, you don't know half of what you'd like to think you know
about C++ programming.


2) If each class ensures that its requirements are met, there can never
be a case where the requirements are not met (robustness)

No. If you wanted robustness you'd wrap the startup/shutdown in a
singleton. You're doing a lot more work than you need to.
3) Once again you are being wantonly obtuse.

No, you keep mixing up Factory patterns claiming that they're only ever
used with State machines - you obviously have read through the GOF book
ONCE, and have definite misconceptions about what those patterns are.

Here's a refresher course for you:
http://www.codeproject.com/csharp/csdespat_1.asp

Read the Factory Pattern section. Do not claim I'm being obtuse until you
have read the Factory Pattern section. Do not claim I'm being obtuse until
you agree that Factory Pattern is a Creational pattern, not a Behavioral
pattern. Do not claim I'm being obtuse until you read the Design Patterns
book again, instead of arguing out of your ass.
4) Now repeat after me "If a parent class has its own objects that it
needs to clean up, the destructor on the parent cannot be virtual."
Appearantly you don't understand that. I feel sorry for your employer.

Ian, you're a ****ing moron who probably learned C++ from the back of a
crackerjack box, and I'd love to talk to your employer and let them know
what a worthless, egotistical dumbshit they have working for them.

Come back after you've run your own tests, and after you've read through
the C++ spec and seen how virtual destructors work.

YOU'VE GOT THEM ALL WRONG YOU IDIOT.
5) Allocation of memory is not the only problem. It takes a considerable
amount of time to create complex objects. Hence, it is important to
precreate complex objects. It is also good practice to precreate all the
objects that are required so that they do not need to be created on the
fly. I guess that you're just another Windows programmer who thinks that
being frugal with resources is not important. You can always throw a
bigger processor at the problem.

I guess you're yet another dickwad who doesn't know the first thing about
programming C++, and probably doesn't know much about speed vs. memory vs.
startup-time constraints when tuning code for performance.

How can you precreate "all objects that are required" if you don't know how
many there will be yet?

I'd love to see what your code looks like. You sound like a BASIC
programmer who uses Global variables for everything.
6) If you return a NULL, it is necessary to test for NULL or expect the
code to crash. If there can never be a case where an invalid object can be
returned then in the case of failure, the returned object can provide the
failure behaviour. Once again, I guess that that is too complex a concept
for you. You would rather crack nuts with a sledge hammer.

The point of failure should be as close as possible to the point where the
error occurs. Your method does not give you that. It gives you a false
sense of robustness which your way of doing it does not give. If you like
sugar-coated security blankets which cause more problems than they solve,
be my guest.

However, you've proved repeatedly that you don't know your ass from your
elbow when it comes to C++ programming, so why don't you shut up and go
learn some more before criticizing those who are more experienced than you
and who know more than you do.
7) Exceptions are very costly in resources and further more many operating
systems do not support them (Include Windows CE here). Even where
exceptions are available they should only be used in exceptional
circumstances.

Windows CE does indeed support exceptions - either using the SEH mechanism,
or using C++'s exceptions.

http://msdn.microsoft.com/msdnmag/issues/02/07/WindowsCENET/

How do you know that exceptions are very costly, Ian? You do know that if
exceptions are not fired, they don't cost anything at all, don't you? Maybe
a little code here and there to do stack unwinding, but nothing that you
don't already get with SEH.

By the way, just what IS your definition of an "exceptional circumstance"?
The printer is on fire? Nuclear war? The coming of the four horsemen of the
apocalypse? The Rapture? The Rupture?
8) Good real-times fail over a problem and then carry on. There is
generally no operator there to enjoy the BSOD which you initiated.

Really? You must have a different definition as to what constitutes a
"good" real time system than I do then. In my world, when I can't correct
deserialize data from a file, I have to stop. I can't just say "Oh well, I
probably didn't need that data anyway" and carry on. That way you end up
with a piece of silicon that's only good for use as a hand-warmer.

When errors occur, you need to log them and/or report them. You can't let
other code assume that everything is still hunky dory. That's fine for
parsers - it's not fine for critical systems.
9) I understand fully how new operates, but every time you create an
object it costs time and resources. More over ever object on the heap is a
potential memory leak if things go wrong.

Ah! You ARE a glorified BASIC programmer. Everything IS a statically
allocated global variable to you.
10) As I've pointed out the parameterized factory makes the great core for
a state machine. I suggest that you look at Gamma p114 and you see that
the pattern is used for creating the new state for some form of game, in
the Sample Code.

Good for them. Now look at the section where the Factory pattern is
introduced, idiot.
There are also other important things to be remembered with any code being
written.
a) Within the lifetime of any complex system there are likely to be a
number of changes in the environment in which it needs to work. It is
therefore important not to write code that can only be run on one platform.

.... unless you need to approach the limits of the capabilities of that
platform, in which case it's highly warranted.

I'd love to see you write an interrupt handler for a Coldfire CPU that'll
also run on a Z80.
b) Code will always be asked to do more than it was originally designed
for. It is hence important to not waste resources as they will probably be
needed by the live system.

It is nice to see that you have a alot of room for improvement. It is
never too late to start.

Ian, I'm putting this nicely:

You don't know shit.

Go learn more about C++ programming. Obviously you started learning at some
point and then stopped. You obviously don't know much about the real world
at all.
 
K

Kevin Goodsell

[Followups set to comp.lang.c++]

So here's some remedial C++ training for you.

class Object {
public:
virtual ~Object()
{
printf("Object Destructor called\n");
};

All these semi-colons after function bodies are unnecessary. Possibly an
error, but I don't know -- semi-colons like this are harmless in some
cases, errors in others, and compilers don't always complain when they
should. I don't know the exact rules, I just leave out the semi-colons
except where they are required.
}

class Table : public Object
{
public:
virtual ~Table()
{
printf("Table Destructor called\n");
};
}

class IKEAGlassTop : public Table
{
char* pMemory;

public:
IKEAGlassTop()
{
pMemory = new char[1000];
}

virtual ~IKEAGlassTop()
{
printf("IKEAGlassTop Destructor called\n");
delete pMemory;

You need delete[] here.

delete [] pMemory;
};
}

So I create a new IKEAGlassTop table, and pass it to something else which
understands Table objects. What happens when the destructor on my Table is
now called?

Undefined behavior due to the use of the wrong delete operator.

I assume you are doing something like this (it's not completely clear,
but this would make the most sense):

Table *my_table = new IKEAGlassTop;

// ...

delete my_table;

Or possibly 'delete some_copy_of_my_table;', but the point is that the
type of the delete operand is still Table*, and it still points to the
thing allocated above.
Answer:
If I have a virtual destructor, there is a vtable lookup, which locates the

That's a possibility, but how virtual calls are implemented is an
implementation detail. There's no requirement that vtables are used.
IKEAGlassTop destructor, which is then called, deleting the memory
allocated when IKEAGlassTop was created. Then, the Tablet destructor is
called, followed by the Object destructor. You get the following output:

IKEAGlassTop Destructor called
Table Destructor called
Object Destructor called

Now... what happens if you do the same without a virtual destructor?

Undefined behavior. Your description is one possibility, but there are
infinitely many others, all equally valid.

Your point is sound, though. Virtual destructors are required in class
hierarchies if you expect to be able to 'delete' an object through a
pointer to one of its base objects and get well-defined results. I'm
sure there's at least one other case where virtual destructors are
required, but I can't think of it right now.

I'm not sure I totally understand Mr. Hilliard's claims, but it
certainly sound like he's confused about virtual destructors. In particular:

This is blatantly wrong, as any decent C++ book can verify. The
comp.lang.c++ FAQ should have some info also... this for example:

http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.5

-Kevin
 
C

cody

Your point is sound, though. Virtual destructors are required in class
hierarchies if you expect to be able to 'delete' an object through a
pointer to one of its base objects and get well-defined results. I'm
sure there's at least one other case where virtual destructors are
required, but I can't think of it right now.

I'm not sure I totally understand Mr. Hilliard's claims, but it
certainly sound like he's confused about virtual destructors. In particular:

This is blatantly wrong, as any decent C++ book can verify.

I think know what Ian means:

class A
{
void *p;
virtual ~A(){prinff("~A");delete[]p;}
}

class B:A
{
virtual ~B(){prinff("~B");}
}

He thinks that if I delete a B-Object the A dtor is not called, since it
overrides the A-dtor.
But this is not true, although the B-dtor overrides the A dtor, the dtor of
the baseclass is always implicitly called.
so:

A* a = new B():
delete a;

outputs:
~B
~A
 
T

The Ghost In The Machine

In comp.os.linux.advocacy, cody
<[email protected]>
wrote
I don't like the fact that you can have different code paths in a method
and have multiple return statements. To me, it would be more orthogonal
if a method could only have one return statement.

Java has the exact same problem.

I'll admit to some curiosity but one way around this issue may be
at the code generation level of the compiler: briefly, if one codes
[..]
where only one RET is in the routine -- and the compiler is responsible
for ensuring that each other return statement has R0 loaded properly
and jumping thereto.

(The assembly language is a corruption of VAX assembly, which I happen
to like. MOV a, b stores a into b. '#' indicates immediate.
R0 and R1 are registers.

Since when does the JVM use registers? It is stackbased.

Ah...I was not aware of that. Not that it matters; the issue was
the RET flow-of-control; one can easily replace a MOV to R0 with
a PUSH onto the numeric stack -- assuming the JVM bothers with
separate stacks; I'd have to look.
 
A

Alexandre Gomes

John said:
The c# *return* statement has been bothering me the past few months.

I don't like the fact that you can have different code paths in a method
and have multiple return statements. To me, it would be more orthogonal
if a method could only have one return statement.

I understand your point.
As it were said before you only use multiple return paths if you want.
There is many ways (someone said that with exception there wasn't a way,
but there is always a way :p) to do it without multiple return paths.
You can simply create a variable to store the value until the end of the
file, use another ones to control if there was an exception etc...

But the my point here is that without multiple return paths you can't do
clear code in many cases. You need too many if's and things like that
(lots of nested if's!!!).

I like it that way mainly because the return not only sets the value to
be returned but also it finishes the flow in the function.
 
J

John Bailo

AlexS said:
to Bailo:

there are lots of facts which people do not like.
However, when fire starts you definitely would prefer several exits
instead of one

Or not?

THat is the function of multiple and well scoped exception handling.

Not the job of a return statement.

Having multiple return statements is like having the GOTO statement in
basic.

And by the way, I dont like break; statements either.

And I don't like having to use the break statement in a switch to exit one
of the cases. Break to me is like hitting ctrl-c in the middle of program
execution. What should happen always is that some variable is set that
then makes the logic of the conditional statement allow it to exit.
 
A

AlexS

to Bailo:

there are lots of facts which people do not like.
However, when fire starts you definitely would prefer several exits instead
of one

Or not?

Alex
 
K

Kev Sherratt

OK, I'll bite..

THat is the function of multiple and well scoped exception handling.
Not the job of a return statement.

...and if you need to terminate the method on an exception because your logic
is now in an indeterminate state? What would you do? Have a variable
declared so you know an error occurred on exit from the handler that you
immediately check and skip to the end? Would you have a variable for each
exception or error that occurs? What happens when an error occurs when
you're in the process of setting the variable that controls flow? Another
variable? Ad nauseum?

Having multiple return statements is like having the GOTO statement in
basic.

Hmm, a bit of a bad analogy there. I'd argue having multiple goto
statements in C# is like having multiple goto statements in BASIC. Of
course, if you have only one return statement, you're effectively issuing a
goto to skip redundant code. I think I'm missing your point here.

And by the way, I dont like break; statements either.

Hmm, doesn't surprise me.. :) You could always not use them, but I suspect
your code won't function as you'd like.

And I don't like having to use the break statement in a switch to exit one
of the cases. Break to me is like hitting ctrl-c in the middle of program
execution. What should happen always is that some variable is set that
then makes the logic of the conditional statement allow it to exit.

Again, bad analogy - so I'll counter. Something like Application.Exit() is
akin to ctrl-c, not a break. Going back to the goto argument, that's
effectively all the break is. Your point here about a variable set to
control logic already applies - it's just that the variable is always true
for the condition. What you seem to be arguing for is wasteful at best;
what condition would you test for exactly? The block of code applicable to
the case in question is finished - why do you want to explicitly test that
it's finished when you already know?

Silly question I know, but have you considered not using C#? :) You could
always use that well known language that doesn't allow multiple return paths
and doesn't support a case/break scenario...

K.
 

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

Top