Table of Contents
C - References and Constants
What is a reference?
A reference is nothing more than an alias. Anything you do with a reference you are actually doing to the object you are referencing.
float x=3.14; float& intref=x; // intref is a reference to x float* ptr=&intref; // ptr holds the address of x float y=2.58; intref=y; // x is now equal to 2.58
Another simple example:
void func(float& fr) { fr=99.99; } int main() { float x=3.14; cout << x << endl; // prints 3.14 func(x); cout << x << endl; // prints 99.99 return 0; }
A class object (i.e. not a built-in object) may be returned by reference or passed by reference for better efficiency.
- Temporary objects call constructors and destructors (which is inefficient).
- A class object passed/returned by reference avoids creation of temporary objects.
- Passing/returning by reference doesn't create temporary objects because a reference is nothing more than an alias to an object that already exists.
Given:
class Book { //... }; class Backpack { public: Book& getContents(); void setContents(Book& bk); private: Book myText; }; Book& Backpack::getContents() { return myText; } void Backpack::setContents(Book& bk) { myText=bk; }
The member functions getContents() and setContents() use references to avoid inefficient temporaries:
int main() { Book fiction; Book somebook; Backpack myPack; myPack.setContents(fiction); // in setContents source, bk is a reference // to fiction, not an inefficient temporary somebook=myPack.getContents(); // myText is returned by reference return 0; }
When used properly, returning by reference allows the function to appear on the left-hand side of an expression.
float& max(float& lhs, float& rhs) { if(lhs>rhs) return lhs; else return rhs; } int main() { float x=2.58; float y=3.14; max(x,y)=99.99; cout << x << endl; // prints 2.58 cout << y << endl; // prints 99.99 return 0; }
Passing/returning by reference allows the referenced object to be modified.
class Backpack { public: Book& getContents(); void setContents(Book& bk); private: Book myText; }; Book& Backpack::getContents() { return myText; } void Backpack::setContents(Book& bk) { myText=bk; Book x; bk=x; //change the object bk refers to } int main() { Book fiction; Book nonfiction; Backpack myPack; myPack.setContents(fiction); // fiction will be the same as "x" in the // function definition myPack.getContents()=nonfiction; // myText is assigned the contents of // nonfiction return 0; }
If the intention of the class designer is to avoid modification of the passed/returned object, the reference should be made const.
class Backpack { public: const Book& getContents(); void setContents(const Book& bk); private: Book myText; }; const Book& Backpack::getContents() { return myText; } void Backpack::setContents(const Book& bk) { myText=bk; Book x; bk=x; //This line is now illegal and won't compile. } int main() { Book nonfiction; Backpack myPack; myPack.getContents()=nonfiction; // This line is now illegal and // won't compile return 0; }
If a member function will not modify its data members, make the member function const.
class Backpack { public: const Book& getContents() const; void setContents(const Book& bk); private: Book myText; };
Note that the setContents() member function is not made const because it modifies the object's data members (it changes myText). It is illegal to modify any object or reference that is declared const. Therefore, constant objects and references may only call constant member functions.
class Backpack { public: const Book& getContents() const; void setContents(const Book& bk); private: Book myText; }; int main() { Backpack bp; const Backpack cbp; cbp.setContents(bp); // This line is illegal because cbp may only call // constant member functions (like getContents) return 0; }
Even if the member function does not actually modify the const object, the const object cannot call the function if it isn't declared as a constant member function.