Skip to main content

  | मौसम…

411 समाचार
Public archive edition · Attributed source reporting with original dates · AI drafts are excluded

411 REFERENCE · Wikibooks

C++ Programming/Code/Statements/Functions/Returning Values

Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.

Returning values When declaring a function, you must declare it in terms of the type that it will return, this is done in three steps, in the function declaration, the function implementation (if distinct) and on the body of the same function with the return keyword. Functions with results You might have noticed by now that some of the functions yield results. Other functions perform an action but don't return a value. Other ways to get a value from a function is to use a pointer or a reference as argument or use a global variable Get more that a single value from a function The return type determines the capacity, any type will work from an array or a std::vector, a struct or a class, it is only restricted by the return type you chose. That raises some questions What happens if you call a function and you don't do anything with the result (i.e. you don't assign it to a variable or use it as part of a larger expression)? What happens if you use a function without a result as part of an expression, like newLine() + 7? Can we write functions that yield results, or are we stuck with things like newLine and printTwice? The answer to the third question is "yes, you can write functions that returns values,". For now I will leave it up to you to answer the other two questions by trying them out. Any time you have a question about what is legal or illegal in C++, a first step to find out is to ask the compiler. However you should be aware of two issues, that we already mentioned when introducing the compiler: First a compiler may have bugs just like any other software, so it happens that not every source code which is forbidden in C++ is properly rejected by the compiler, and vice versa. The other issue is even more dangerous: You can write programs in C++ which a C++ implementation is not required to reject, but whose behavior is not defined by the language. Needless to say, running such a program can, and occasionally will, do harmful things to the system it is running or produce corrupt output! For example: int MyFunc(); // returns an int SOMETYPE MyFunc(); // returns a SOMETYPE int* MyFunc(); // returns a pointer to an int SOMETYPE *MyFunc(); // returns a pointer to a SOMETYPE SOMETYPE &MyFunc(); // returns a reference to a SOMETYPE If you have understood the syntax of pointer declarations, the declaration of a function that returns a pointer or a reference should seem logical. The above piece of code shows how to declare a function that will return a reference or a pointer; below are outlines of what the definitions (implementations) of such functions would look like: SOMETYPE *MyFunc(int *p) { //... return p; } SOMETYPE &MyFunc(int &r) { //... return r; } return Return "codes" (best practices) There are 2 kinds of behaviors : The selection of, and consistent use of this practice helps to avoid simple errors. Personal taste or organizational dictates may influence the decision, but a general rule-of-thumb is that you should follow whatever choice has been made in the code base you are currently working in. However, there may be valid reasons for making a different choice in any particular situation. Positive means success This is the "logical" way to think, and as such the one used by almost all beginners. In C++, this takes the form of a boolean true/false test, where "true" (also 1 or any non-zero number) means success, and "false" (also 0) means failure. The major problem of this construct is that all errors return the same value (false), so you must have some kind of externally visible error code in order to determine where the error occurred. For example: bool bOK; if (my_function1()) { // block of instruction 1 if (my_function2()) { // block of instruction 2 if (my_function3()) { // block of instruction 3 // Everything worked error_code = NO_ERROR; bOK = true; } else { //error handler for function 3 errors error_code = FUNCTION_3_FAILED; bOK = false; } } else { //error handler for function 2 errors error_code = FUNCTION_2_FAILED; bOK = false; } } else { //error handler for function 1 errors error_code = FUNCTION_1_FAILED; bOK = false; } return bOK; As you can see, the else blocks (usually error handling) of my_function1 can be really far from the test itself; this is the first problem. When your function begins to grow, it's often difficult to see the test and the error handling at the same time. This problem can be compensated by source code editor features such as folding, or by testing for a function returning "false" instead of true. if (!my_function1()) // or if (my_function1() == false) { //error handler for function 1 errors //... This can also make the code look more like the "0 means success" paradigm, but a little less readable. The second problem of this construct is that it tends to break up logical tests (my_function2 is one level more indented, my_function3 is 2 levels indented) which causes legibility problems. One advantage here is that you follow the structured programming principle of a function having a single entry and a single exit. The Microsoft Foundation Class Library (MFC) is an example of a standard library that uses this paradigm. 0 means success This means that if a function returns 0, the function has completed successfully. Any other value means that an error occurred, and the value returned may be an indication of what error occurred. The advantage of this paradigm is that the error handling is closer to the test itself. For example the previous code becomes: if (0 != my_function1()) { //error handler for function 1 errors return FUNCTION_1_FAILED; } // block of instruction 1 if (0 != my_function2()) { //error handler for function 2 errors return FUNCTION_2_FAILED; } // block of instruction 2 if (0 != my_function3()) { //error handler for function 3 errors return FUNCTION_3_FAILED; } // block of instruction 3 // Everything worked return 0; // NO_ERROR In this example, this code is more readable (this will not always be the case). However, this function now has multiple exit points, violating a principle of structured programming. The C Standard Library (libc) is an example of a standard library that uses this paradigm. Some people argue that using functions results in a performance penalty. In this case just use inline functions and let the compiler do the work. Small functions mean visibility, easy debugging and easy maintenance. C++ Programming