Friday, January 28, 2011

Prefer Pass by Reference Over Pass by Value In C++.



In C, everything is passed by value, and C++ also adopting the pass-by-value convention as its default. The meaning of passing an object by value is defined by the copy constructor of that object's class. So pass-by-value is an extremely expensive operation.

For example:-

class Person {
public:
//Base Class Constructor
Person();
//Base Class Destructor
~Person();
..................//Some code
private:
string p_name, p_address;
};
class Employee: public Person {
public:
//Derived Class Constructor
Employee();
//Base Class Destructor
~Employee();
.................. //Some Code
private:
//Default String Class Object is created
string Dept, Company;
};
Now consider a simple function setEmployee that takes a Employee argument as by value and immediately returns it also as value, plus a call to that function:

Employee setEmployee(Employee s) { return s; }
Employee emp_v; // Employee emp_v is working



Here in above expression, the Employeecopy constructor is called to initialize "s". Then the Employee copy constructor is called again to initialize the object returned by the function with s. Next, the destructor is called for s. Finally, the destructor is called for the object returned by setEmployee(). So the cost of this do-nothing function is two calls to the Employee copy constructor and two calls to the Employee destructor. Also, a Employee object has two string objects within it, so every time you construct a Employee object you must also construct two string objects. A Employee object also inherits from a Person object, so every time you construct a Employee object you must also construct a Person object. A Person object has two additional string objects inside it, so each Person construction also entails two more string constructions. The end result is that passing a Employee object by value leads to one call to the Employee copy constructor, one call to the Person copy constructor, and four calls to the string copy constructor. When the copy of the Employee object is destroyed, each constructor call is matched by a destructor call, so the overall cost of passing a Employee by value is six constructors and six destructors. Because the function setEmployee uses pass-by-value twice (once for the parameter, once for the return value), the complete cost of a call to that function is twelve constructors and twelve destructors!

In fairness to the C++ compiler-writers , this is a worst-case scenario. Compilers are allowed to eliminate some of these calls to copy constructors. To avoid this potentially expensive cost, you need to pass things not by value, but by reference:

const Employee& setEmployee(const Employee& s)
{
return s;
}

This is much more efficient: no constructors or destructors are called, because no new objects are being created. Passing parameters by reference has another advantage: it avoids what is sometimes called the "slicing problem." When a derived class object is passed as a base class object, all the specialized features that make it behave like a derived class object are "sliced" off, and you're left with a simple base class object. This is almost never what you want.

For example, suppose you're working on a set of classes for implementing a Employee management system:

class Person {
public:
string name() const; // return name of Person
virtual void display() const; // draw Person and contents
};
class Employee: public Person {
public:
virtual void display() const;
};

"Display" is virtual function having different definition in both base class and derived class and can be called from another function.
Example:-

// a function that suffers from the slicing problem
void printNameAndDisplay(Person per)
{
cout << per.name(); per.display(); } Consider what happens when you call this function with a Employee object: Employee emp; printNameAndDisplay(emp); As a Person object, and all the specialized information that made "emp" act like a Employee object will be sliced off. Inside printNameAndDisplay, "per" object will always act like an object of class Person, regardless of the type of object that is passed to the function. In particular, the call to display inside printNameAndDisplay will always call Person::display, never Employee::display.
The way around the slicing problem is to pass w by reference:

// a function that doesn't suffer from the slicing problem
void printNameAndDisplay(const Person& per)
{
cout << per.name();
per.display();
}

Now per will act like whatever kind of Person is actually passed in. To emphasize that per isn't modified by this function even though it's passed by reference.

Below are the Three different types of parameter passing can be done :-

A f(A x){ return x;}//two Copy Constructor
A g(A& x){return x;}//single Copy Constructor
A& h(A& x){return x;};//no Copy Constructor

Where A is Class and three functions are f(), g() and h() taking different type of Object as parameter.

No comments:

Post a Comment