- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
-
Tips and Techniques
- Using the Swap() Algorithm
- Using class stopwatch for Performance Measurements
- Extending <tt><iostream></tt> to Support User-Defined Types
- Using <tt>auto_ptr</tt> To Automate Memory Management
- Using <tt>auto_ptr</tt> To Automate Memory Management, Part II
- Using <tt>auto_ptr</tt> To Automate Memory Management, Part III
- Using <tt>enum</tt>s as Mnemonic Indexes
- Create Objects on Pre-Allocated Memory Using <tt>Placement-new</tt>
- Online Books: <tt>Placement-new</tt>
- Bitwise Operators
- Bitwise Operators II
- Who's <tt>this</tt>?
- A Reference Guide
- The Virtues of Multiple Inheritance
- Interfaces
- Multiple Inheritance: Construction and Destruction Order
- nothrow new
- POD Initialization
- Object Initialization
- <tt>const</tt> Declarations
- The Semantics of <tt>volatile</tt>
- <tt>inline</tt> Functions
- Project Organization Guidelines
- All About <tt>bool</tt>
- <tt>typedef</tt> Declarations
- State of the <tt>union</tt>
- Dynamic Cast Uses
- Integrating C and C++
- <tt>const</tt>-Correctness
- <tt>const</tt>-Correctness: Advanced Issues
- Sprucing Up Legacy Code
- Virtual Constructors
- Naming Names
- Function Calls
- Speaking Standardese (updated)
- Speaking Standardese: the One Definition Rule
- Declarations and Definitions
- More on Declarations and Definitions
- The Most Vexing Parse
- Finally, At Last
- Sound Bytes (Admittedly Off Topic)
- Local Classes
- Complex Arithmetic
- Floating Point Woes
- String Manipulation
- The Object Model
- The Object Model II
- The Object Model III
- Temporary Objects
- Temporary Objects: Advanced Techniques
- Over-Engineering
- Security Enhancements
- Drop the (automatic) Pilot
- Choosing the Right Container
- Choosing the Right Container II
- Choosing the Right Container, Part III
- Arrays and Pointers
- Low-Level File I/O
- Low-Level File I/O Part II
- static Declarations, Part I
- static Declarations, Part II
- <code>static</code> Initialization Order
- Revisiting the Deprecation of File-Scope Static
- Virtual Memory and Memory Mapping
- Cellular Phone Programming Guidelines
- The Handle/Body Idiom
- Whole Program Optimization, Part I
- Whole Program Optimization, Part II
- Manipulating Directories
- Window Dressing
- <code>friend</code> Declarations
- <code>friend</code> Part II: the Interaction of Friendship and Template Classes
- Forcing Object Allocation on Specific Storage Types
- Lazy Evaluation
- Cache and Carry
- Controlling a Container’s Capacity
- Non-Blocking I/O, Part I
- Non-Blocking I/O, Part II
- Using Unions for Automatic Conversion
- Launching a Child Process
- <tt>switch</tt> Statements
- Introducing the "struct Hack"
- Scoped Enumerators
- Doing Statistics with STL
- Fixing the "Unresolved External" Linkage Error
- Understanding Calling Conventions
- Understanding the Empty Base Optimization
- Implementing RPC with the door Library, Part 1
- Implementing RPC with the door Library, Part 2
- Eliminating Two Common Pointer and <tt>sizeof</tt> Bugs
- Command Line Arguments
- Performance Myths Busting
- Tag Names And Types Part I
- Tag Names And Types Part II
- The Infamous goto
- Trimming Strings
- Can Objects Live Forever? Part I
- Can Objects Live Forever? Part II
- Five Ways to Improve Your Functions
- Member Aggregate Initialization
- Five Futile Coding-Style Debates
- The Good Parasite Idiom: An Exercise in OOD
- The Good Parasite Idiom: An Exercise in OOD, Part II
- The Good Parasite Idiom: An Exercise in OOD, Part III
- Ten Techniques to Reduce the Size of Your Classes, Part I
- Ten Techniques to Reduce the Size of Your Classes, Part II: Inheritance Issues
- Ten Techniques to Reduce the Size of Your Classes, Part III
- Ten Techniques to Reduce the Size of Your Classes IV
- Taking the Address of an Object with an Overloaded Operator <tt>&</tt>
- strcpy() -- How and Why Does It "Just Work"?
- Anonymous Structs
- Five Easy Ways to Reduce The Size of your Executables
- Standard Layout Classes and Trivially Copyable Types, Part I
- Standard Layout Classes and Trivially Copyable Types, Part II
- Five Simple Code Sanity Checks
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
- C++0X: The New Face of Standard C++
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
Extending <tt><iostream></tt> to Support User-Defined Types
Last updated Jan 1, 2003.
Legacy applications and diehards still use C's <stdio.h> for performing I/O tasks. Yet, the library suffers from several limitations that make it unsuitable for use in object-oriented environments. For instance, you can't extend <stdio.h> to support user-defined types.
Unlike <stdio.h>, <iostream> classes and objects are extensible. In the following sections, I will you show how to extend <iostream> components by overloading operator << so that it can display a user-defined object.
Supporting Built-in and Standard Types
The most widely used objects of the <iostream> library are cin and cout, which perform console-oriented input and output, respectively. Note that cin and cout are real objects, not classes, which C++ instantiates before the program's outset.
These objects define several overloaded versions of the operators >> and << for data input and output. The overloaded versions support all the built-in types of C++ such as int, double and bool. In addition, they support various types of pointers: char * (i.e., C-strings), void * (for pointers in general) and some of the Standard Library's classes, including std::string and std::complex.
For this reason, the following program will run smoothly on any standard-compliant C++ implementation:
#include <iostream>
#include <string>
#include <complex>
using namespace std;
int main()
{
complex <double> comp(1.0, 0.0);
cout<<comp<<endl;
string s;
const char * msg="enter your name: ";
cout<<msg<<endl;
cin>>s;
bool b= true;
void *p = &b;
cout<<"b's value is: "<<b<<", b's address is: "<<p<<endl;
}
The output from this program should look more or less as follows:
<1,0> enter your name: Danny b's value is: true, b's address is: 0012ff3b
This is all well and good. Not only do cin and cout support a vast set of data types and standard classes, such as std::string, but you're free from the burden of using cumbersome format flags to indicate the type of an argument in a cout expression. However, suppose you declared the following class:
class student
{
private:
string name;
string department;
public:
student(const string& n, const string& dep)
: name(n), department(dep) {}
student() {} //default ctor
string get_name() const { return name; }
string get_department () const { return department; }
void set_name(const string& n) { name=n; }
void set_department (const string& d) {department=d;}
};
And you want to be able to use it in a cout statement as follows:
student st("James E.", "Biology");
cout<<st; // display student's details
The cout expression will cause a compilation error, because there's no overloaded version of << that supports student.
Overloading cout's <<
To extend cout's support for a user-defined type, you need to overload the operator << of class ostream (cout is an instance of ostream). The canonical form of such an overloaded << looks as follows:
ostream& operator << (ostream& os, const student& s);
In other words, an overloaded << returns a reference to an ostream object, and takes two parameters by reference: an ostream object and a user-defined type. The user-defined type is declared const parameter because the output operation doesn't modify it.
The body of the overloaded << inserts members of the user-defined object which cout does support into the ostream object. In this case, the two data members of student are of type std::string. As previously shown, cout natively supports this class:
os<<s.get_name()<<'\t'<<s.get_department()<<endl;
Make sure that the members inserted are separated by a tab, newline, or spaces. Otherwise, they will appear as if they were concatenated when displayed on the screen. Remember also to place the endl manipulator at the end of the insertion chain to force a buffer flush. Finally, the overloaded operator should return the ostream object into which the members have been inserted. This enables you to chain several objects in a single cout statement, like this:
student s1("Bill","CS"), s2("Jane", "Linguistics";
cout<<s1<<s2; // chaining multiple objects
The complete definition of the overloaded << operator is as follows:
ostream& operator << (ostream& os, const student& s)
{
os<<s.get_name()<<'\t'<<s.get_department()<<endl;
return os;
}
The insertion operations and the return statement can be accomplished in a single statement:
ostream& operator << (ostream& os, const student& s)
{
return os<<s.get_name()<<'\t'<<s.get_department()<<endl;
}
Now you can use the overloaded << in your code:
int main()
{
student s1("Bill F.","CS"), s2("Jane E.", "Linguistics");
cout<<s1<<s2; // chaining multiple objects
}
As expected, this program displays:
Bill F. CS Jane E. Linguistics
Overloading cin's <<
At this point you're probably wondering whether it's possible to extend cin as well, so that it can read an entire object from the standard input in a single cin expression. The answer is Yes. Although this technique is less commonly used, you can define an overloaded version of operator << that reads the values of student's data members from the standard input, like this:
istream& operator >> (istream& is, student & stu)
{
string name, dep;
cout<<"enter name: ";
cin>>name;
stu.set_name(name);
cout<<"enter department: ";
cin>>dep;
stu.set_department(dep);
return is; //enable chaining
}
student s1, s2;
cin>>s1>>s2; //fill the objects
cout<<s1<<s2;
Summary
For trivial classes such as student, you don't really have to define a specialized version of operator <<. Instead, you can access its two data members through student's get_name() and get_department() or set_name() and set_department() in the case of operator <<. However, for more complex classes that have dozens of data members, it's advisable to overloaded << and >> and thus spare users the trouble of having to extract or insert every data member individually.
