BogoToBogo
  • Home
  • About
  • Big Data
  • Machine Learning
  • AngularJS
  • Python
  • C++
  • go
  • DevOps
  • Kubernetes
  • Algorithms
  • More...
    • Qt 5
    • Linux
    • FFmpeg
    • Matlab
    • Django 1.8
    • Ruby On Rails
    • HTML5 & CSS

C++ Tutorial - Object Returning - 2020

cplusplus_icon.png




Bookmark and Share





bogotobogo.com site search:




Object Returning

When a function, either a member function or a standalone function, returns an object, we have choices.


The function could return

  1. A reference to an object
  2. A constant reference to an object
  3. An object
  4. A constant object



Returning a Reference to a const object

Though the main reason for using const reference is efficiency, there are restrictions on when this choice could be used. If a function returns an object that is passed to it, either by object invocation or as a method argument, we can increase the efficiency of the method by having it pass a reference.

For example, suppose we want to write a function Maximum() that returns the larger of two Complx objects, where the Complx is defined as below.

#include <iostream>
#include <cmath>

using namespace std;

class Complx {
private:
	double real;
	double imag;
public:
	Complx() {}
	Complx(double r, double i): real(r), imag(i) {}
	Complx  operator+(const Complx & c) const {
		return Complx(real + c.real, imag + c.imag);
	}
	Complx & operator=(const Complx &c;) {
		real = c.real;
		imag = c.imag;
		return *this;
	}

	friend ostream& operator<<(ostream &os;, const Complx &c;);
	double size() const {
		return sqrt(real*real + imag*imag);
	}
};

ostream & operator<<(ostream &os;, const Complx & c) {
	os << "(" << c.real << "," << c.imag << ")";
	return os;
}

/*
Complx Maximum(const Complx &c1;, const Complx &c2;) {
	
	if (c1.size() > c2.size()) 
		return c1;
	else
		return c2;
}
*/

const Complx & Maximum(const Complx &c1;, const Complx &c2;) {
	
	if (c1.size() > c2.size()) 
		return c1;
	else
		return c2;
}

int main( ) 
{ 
	Complx c1(10,30);
	Complx c2(13,25);
	Complx mx = Maximum (c1,c2);

	cout << " c1 = " << c1 << endl;
	cout << " c2 = " << c2 << endl;
	cout << "Maximum(c1,c2) = " << mx << endl;

	Complx c3 (20,40);
	Complx c4,c5;
	c5 = c4 = c3;

	cout << c4 << " got its value from c3" << endl;

	Complx c6 = c1 + c2;
	cout << " c6 (c1+c2) = " << c6 << endl;

	Complx c7;
	c1 + c2 = c7;
	return 0; 
}

Output is:

 c1 = (10,30)
 c2 = (13,25)
Maximum(c1,c2) = (10,30)
(20,40) got its value from c3
 c6 (c1+c2) = (23,55)

Either of the following two implementations for Maximum() would work:

// version #1
Complx Maximum(const Complx &c1;, const Complx &c2;) {
	
	if (c1.size() > c2.size()) 
		return c1;
	else
		return c2;
}

// version #2 : This is our choice 
const Complx & Maximum(const Complx &c1;, const Complx &c2;) {
	
	if (c1.size() > c2.size()) 
		return c1;
	else
		return c2;
}

There are three points that can be emphasized:

  1. Returning an object invokes the copy constructor while returning a reference doesn't. So, the version #2 does less work and is more efficient.
  2. The reference should be to an object that exists when the calling function is execution. In this example, the reference is to either c1 or c2, and both are objects defined in the calling function, so the requirement is met.
  3. Both c1 or c2 are declared as being const references, so the return type has to be const to match.


Returning a Reference to a Non-const Object

There are two common examples of returning a non-const object

  1. Overloading the assignment operator
  2. Overloading the << operator for use with cout

The return value of operator==() is used for chained assignment:

Complx c3 (20,40);
Complx c4,c5;
c5 = c4 = c3;
In the code, the return value of c4.operator=(c3) is assigned to c5. Returning either a Complx object or a reference to a Complx object would work. But using a reference allows the function to avoid calling the Complx copy constructor to create a new Complx object. In this case, the return type is not const because operator=() method returns a reference to c4, which is modified.

The return value of operator<<() is used for chained output:

cout << c4 << " got its value from c3" << endl;

In the code, the return value of operator<<(cout,c4) becomes the object used to display the string " got its value from c3". Here, the return type must be ostream & and not just ostream. Using an ostream return type would require calling the ostream copy constructor, and, as it turns out the ostream class does not have a public copy constructor. Fortunately, returning a reference to cout poses no problems because cout is already in scope of the calling function.

Also, look at another case when we need to return a reference to an object related to the memory allocation in the constructor and copy constructor.





Returning a Non-const Object

If the object being returned is local to the called function, then it should not be returned by reference because the local object has its destructor called when the function terminates. This kind of memory management is called automatic memory management, and it is associated with local variables/objects. A local object occupies memory that the system allocates. The system automatically deallocates that memory at the end of the block that contains the definition. So, when control returns to the calling function, there is no object left to which the reference can refer or the pointer can point to. When the function returns, it's the end of the execution block that contains the definition of the reference or pointer. Because of the deallocation, the reference/pointer is now invalid, but the function tries to return it any way. What would happen? It's anybody's guess.

In these circumstances, we should return an object, not a reference. Never Retrun a reference to a local object.

For example, assume we have a function returning a local object like this:

const Complx &complxReturn;(const Complx & c) {
		Complx complxObj = c;
		return complxObj;
}

When the function completes, the storage in which the local objects were allocated is freed. A reference to a local object refers to undefined memory after the function terminates. So, the function may fail at run time because it returns a reference to a local object. The functions ends, the storage in which complxObj resides is freed. The return value refers to memory that is no longer available to the program.

Usually, overloaded arithmetic operators fall into this category. In our example, we have overloaded operator+:

Complx  operator+(const Complx & c) const {
	return Complx(real + c.real, imag + c.imag);
}

When we do

Complx c6 = c1 + c2;

The value being returned is not c1, which should be left unchanged by the process, nor c2, which should also be unaltered. Thus the return value can't be a reference to an object that is already present in the calling function. Instead, the sum is a new, temporary object computed from Complx operator+(), and the function shouldn't return a reference to a temporary object, either. Instead, it should return an actual object, not a reference:

Complx  operator+(const Complx & c) const {
	return Complx(real + c.real, imag + c.imag);
}

There is the added expense of calling the copy constructor to create the returned object so than the calling function can use this copy of the object, but the extra expense is unavoidable. Using an object return type, however, means the program constructs a copy of Complx before destroying it, and the calling function gets the copy. (See when we call copy constructor: copy constructor)



Returning a const Object

The Complx::operator+() in the example has a strange property. The intended use is this:

Complx c6 = c1 + c2;	// #1

But the definition also allows us to use the following:

Complx c7;
c1 + c2 = c7;	// #2

This code is possible because the copy constructor constructs a temporary object to represent the return value. So, in the code, the expression c1 + c2 stands for that temporary object. In statement #1, the temporary object is assigned to c6. In statement #2, c7 is assigned to the temporary object.

The temporary object is used and then discarded. For instance, in statement #2, the program computes the sum of c1 and c2, copies the answer into the temporary return object, overwrites the contents with the contents of c7, and then discards the temporary object. The original complex numbers are all left unchanged.

If we declare the return type as a const object, we can avoid the problem.

const Complx  operator+(const Complx & c) const {
	return Complx(real + c.real, imag + c.imag);
}



Why const with a reference return?

Why do we use const for a reference return?
Let's look at the following example:

#include <iostream>

struct node
{
	int data;
};

const node & makeNode(node &cref;);

int main()
{
	node nodeA = {0};
	std::cout << "1: nodeA.data = " << nodeA.data << std::endl;
	makeNode(nodeA);
	std::cout << "2: nodeA.data = " << nodeA.data << std::endl;
	node nodeB;
	nodeB = makeNode(nodeA);
	std::cout << "3: nodeA.data = " << nodeA.data << std::endl; 
	std::cout << "1: nodeB.data = " << nodeB.data << std::endl;

	node nodeC;
	// makeNode(nodeB).data = 99;

	return 0;
}

const node & makeNode(const node &ref;)
{
	std::cout << "call makeNode()\n";
	ref.data++;
	const return ref;
}

The makeNode() function return type is const node &. What's the purpose of const? It does not mean that the node structure itself is const; it just means that we can't use the return value directly to modify the structure.

If we omitted const, we could use the code below:

makeNode(nodeB).data = 99;
Because makeNode returns a reference to nodeB, this code is the same as this:
makeNode(NodeB);
nodeB.data = 99;
But because of the const, we can't use the return value directly to modify the structure, and with the const, the code:
makeNode(nodeB).data = 99;
won't compile.




Summary
  1. If a method or function returns a local object, it should return an object, not a reference.
  2. If a method or function returns an object of a class for which there is no public copy constructor, such as ostream class, it must return a reference to an object.
  3. Some methods and functions, such as the overloaded assignment operator, can return either an object or a reference to an object. The reference is preferred for reasons of efficiency.
  4. It is an error to return a pointer to a local object. Once the function completes, the local object are freed. The pointer would be a dangling pointer that refers to a nonexistent object.


DahlMahSan2




Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization

YouTubeMy YouTube channel

Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






C++ Tutorials

C++ Home

Algorithms & Data Structures in C++ ...

Application (UI) - using Windows Forms (Visual Studio 2013/2012)

auto_ptr

Binary Tree Example Code

Blackjack with Qt

Boost - shared_ptr, weak_ptr, mpl, lambda, etc.

Boost.Asio (Socket Programming - Asynchronous TCP/IP)...

Classes and Structs

Constructor

C++11(C++0x): rvalue references, move constructor, and lambda, etc.

C++ API Testing

C++ Keywords - const, volatile, etc.

Debugging Crash & Memory Leak

Design Patterns in C++ ...

Dynamic Cast Operator

Eclipse CDT / JNI (Java Native Interface) / MinGW

Embedded Systems Programming I - Introduction

Embedded Systems Programming II - gcc ARM Toolchain and Simple Code on Ubuntu and Fedora

Embedded Systems Programming III - Eclipse CDT Plugin for gcc ARM Toolchain

Exceptions

Friend Functions and Friend Classes

fstream: input & output

Function Overloading

Functors (Function Objects) I - Introduction

Functors (Function Objects) II - Converting function to functor

Functors (Function Objects) - General



Git and GitHub Express...

GTest (Google Unit Test) with Visual Studio 2012

Inheritance & Virtual Inheritance (multiple inheritance)

Libraries - Static, Shared (Dynamic)

Linked List Basics

Linked List Examples

make & CMake

make (gnu)

Memory Allocation

Multi-Threaded Programming - Terminology - Semaphore, Mutex, Priority Inversion etc.

Multi-Threaded Programming II - Native Thread for Win32 (A)

Multi-Threaded Programming II - Native Thread for Win32 (B)

Multi-Threaded Programming II - Native Thread for Win32 (C)

Multi-Threaded Programming II - C++ Thread for Win32

Multi-Threaded Programming III - C/C++ Class Thread for Pthreads

MultiThreading/Parallel Programming - IPC

Multi-Threaded Programming with C++11 Part A (start, join(), detach(), and ownership)

Multi-Threaded Programming with C++11 Part B (Sharing Data - mutex, and race conditions, and deadlock)

Multithread Debugging

Object Returning

Object Slicing and Virtual Table

OpenCV with C++

Operator Overloading I

Operator Overloading II - self assignment

Pass by Value vs. Pass by Reference

Pointers

Pointers II - void pointers & arrays

Pointers III - pointer to function & multi-dimensional arrays

Preprocessor - Macro

Private Inheritance

Python & C++ with SIP

(Pseudo)-random numbers in C++

References for Built-in Types

Socket - Server & Client

Socket - Server & Client 2

Socket - Server & Client 3

Socket - Server & Client with Qt (Asynchronous / Multithreading / ThreadPool etc.)

Stack Unwinding

Standard Template Library (STL) I - Vector & List

Standard Template Library (STL) II - Maps

Standard Template Library (STL) II - unordered_map

Standard Template Library (STL) II - Sets

Standard Template Library (STL) III - Iterators

Standard Template Library (STL) IV - Algorithms

Standard Template Library (STL) V - Function Objects

Static Variables and Static Class Members

String

String II - sstream etc.

Taste of Assembly

Templates

Template Specialization

Template Specialization - Traits

Template Implementation & Compiler (.h or .cpp?)

The this Pointer

Type Cast Operators

Upcasting and Downcasting

Virtual Destructor & boost::shared_ptr

Virtual Functions



Programming Questions and Solutions ↓

Strings and Arrays

Linked List

Recursion

Bit Manipulation

Small Programs (string, memory functions etc.)

Math & Probability

Multithreading

140 Questions by Google



Qt 5 EXPRESS...

Win32 DLL ...

Articles On C++

What's new in C++11...

C++11 Threads EXPRESS...

Go Tutorial

OpenCV...








Contact

BogoToBogo
contactus@bogotobogo.com

Follow Bogotobogo

About Us

contactus@bogotobogo.com

YouTubeMy YouTube channel
Pacific Ave, San Francisco, CA 94115

Pacific Ave, San Francisco, CA 94115

Copyright © 2024, bogotobogo
Design: Web Master