The default mode of passing variables to functions in C++ is by value.
class Date {
public:
int day;
int month;
int year;
};
void printAs1999( Date d ) {
d.year = 1999;
cout << d.month << "/" << d.day << "/" << d.year << endl;
}
int main() {
Date originalDate;
originalDate.month = 7;
originalDate.day = 20;
originalDate.year = 2010;
printAs1999( originalDate );
cout << "originalDate.year is " << originalDate.year << endl;
}
In this example, we create an instance of the Date class called
originalDate and initialize
originalDate.year to be 2010. When we call the
printAs1999 function, however, a copy of
originalDate is made and called d. The
function modifies d, but since it's just a copy, the
originalDate variable remains unchanged.
The actual work of copying the Date class is done by the copy constructor of the Date class. If we don't define a copy constructor for a class, the compiler defines one for us that does a naive copy of the data in the class. We could define a custom copy constructor for the Date class like this:
class Date {
public:
...
Date( const Date& d );
...
};
Date::Date( const Date& d ) {
month = d.month;
day = d.day;
year = d.year;
}
There are often situations where we don't want to make a copy of a variable when we pass it to a function (i.e. when we want any changes made to the variable in the function to stick, or when copying the variable would take a lot of memory or time). To avoid copying a variable, we can pass it by reference instead of by value, by simply include an ampersand (&) before the variable name in the function header:
void printAs1999( Date& d ) { // the & means pass by reference
d.year = 1999;
cout << d.month << "/" << d.day << "/" << d.year << endl;
}
int main() {
Date originalDate;
originalDate.month = 7;
originalDate.day = 20;
originalDate.year = 2010;
printAs1999( originalDate );
cout << "originalDate.year is " << originalDate.year << endl;
}
In this example, after printAs1999 has been called,
the value of originalDate.year has been changed to 1999.
In order to get the best of both worlds - the efficiency of pass-by-reference and the safety of pass-by-value, many programmers use a const reference:
void printAs1999( const Date& d ) { // safe and efficient
cout << d.month << "/" << d.day << "/" << 1999 << endl;
}
int main() {
Date originalDate;
originalDate.month = 7;
originalDate.day = 20;
originalDate.year = 2010;
printAs1999( originalDate );
cout << "originalDate.year is " << originalDate.year << endl;
}
The const keyword tells the compiler that the
associated variable should not be altered. const can
also be used to describe member functions of a class that don't alter
any data of that class. The following definition of a Date class uses
const everywhere that it would be appropriate, a strategy
known as const
correctness:
#ifndef DATE_H
#define DATE_H
#include <string>
using std::string;
/**
* A nifty class for holding a date in time.
*/
class Date {
public:
/**
* Constructors
*/
Date();
Date( const string& s );
Date( int m, int d, int y );
Date( const Date& d ); // copy constructor
~Date();
/**
* Display the date to standard out
*/
void display() const; // displaying doesn't modify the date, so it's const
/**
* Getters/setters
*/
void setDay( int d );
void setMonth( int m );
void setYear( int y );
int getDay() const; // these getters are const, because they don't change anything
int getMonth() const;
int getYear() const;
private:
int day;
int month;
int year;
};
#endif
#include "Date.h"
#include <iostream>
using std::cout;
using std::endl;
Date::Date() {
// initialize data
}
Date::Date( const string& s ) {
// convert s to m/d/y
}
Date::Date( int m, int d, int y ) {
day = d;
month = m;
year = y;
}
Date::Date( const Date& d ) {
day = d.getDay();
month = d.getMonth();
year = d.getYear();
cout << "Calling copy constructor" << endl;
}
Date::~Date() {
cout << "Destructing Date" << endl;
}
void Date::display() const {
cout << getMonth() << "/" << getDay() << "/" << getYear();
}
/**
* Getters/setters
*/
void Date::setDay( int d ) {
if( d < 0 || d > 31 ) {
cout << "Error! So you had a bad day: " << d << endl;
day = 1;
} else {
day = d;
}
}
void Date::setMonth( int m ) {
month = m;
}
void Date::setYear( int y ) {
year = y;
}
int Date::getDay() const {
return day;
}
int Date::getMonth() const {
return month;
}
int Date::getYear() const {
return year;
}
Arrays in C++ are fairly simple structures:
int int_array1[10];
int int_array2[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int int_array3[10] = {0, 1, 2, 3, 4}; // remaining elements set to 0
cout << int_array1[0]; // displays uninitialized value
cout << int_array2[0]; // displays 0
char char_array1[5];
char char_array2[] = "Hello"; // size is 5 chars + 1 null char
char char_array3[] = {'H', 'e', 'l', 'l', 'o', '\0'}; // same thing
cout << char_array1[0]; // displays uninitialized value
cout << char_array2[0]; // displays H
In C, strings were represented as null-terminated char
arrays. In C++, we have the string
class, which is a bit more useful.