In C you declared a constant using the #define statement. For example, to specify that the boiling point of water is 212 degrees, you might have written:
#define WATER_BOILING_POINT 212
In C++ you declare a constant using the const keyword. For example:
const int WATER_BOILING_POINT = 212;Notice that a constant declaration requires five elements:
The constant keyword can be used to declare local variables, global variables, or parameters to be constant. For example, the function declaration:
float compute_avg(const int scores [], const int size);declares that both the scores array and the size of the scores array are constant within the function. That means that within the function you promise not to modify either the contents of the scores array or the size variable. If you attempt to do so, the compiler will give you an error message. For example:
#include <stdio.h> float compute_avg(const int scores[], int size) { int i, sum; sum = 0; for (i = 0; i < size; i++) { sum += scores[i]; scores[i] = sum; // can't do this--scores is a constant } return ((float)sum / size); } main() { int s[5] = {72, 68, 79, 81, 65}; printf("%6.2f\n", compute_avg(s, 5)); } |
If you try to compile this program, you get the following error message from the compiler:
UNIX> g++ temp.cc avg.cc: In function `float compute_avg(const int *, int)': avg.cc:8: assignment of read-only location
We will try to avoid using reference types in this class because they are confusing. However, they appear in examples in both the Weiss and Schildt books and therefore you need to have at least a basic understanding of what they are. For example, if you have a class named string, you will often see a method with the following signature:
string::string(const string &objToBeCopied);This method is called a copy constructor and we will talk about it later in these notes. The thing to focus on for now is the declaration of the parameter objToBeCopied:
You have already seen how constructors are used to initialize an object when it is first created. Here are some specifics to remember about constructors:
class Stack { protected: int *data; int top; public: Stack( int maxSize ); Stack( ); ... };
Stack::Stack( int maxSize ) { top = 0; data = new int[maxSize]; } Stack::Stack( ) { top = 0; data = new int[20]; // default maximum size } |
~Stack() { delete [] data; } |
Stack a(5); Stack b; Stack c(a);
Stack *x = new Stack(4); Stack *y; Stack *z = new Stack[5]; // allocate an array of 5 Stacks y = new Stack(*x);
delete x; delete [] z;
The copy constructor copies the contents of one object to another object. The two objects must be members of the same class.
string a; string c(a); // or string c = a;
As an example, suppose you have a string class:
class string { public: string(const string&); protected: char *str; int length; }Then the copy constructor should look something like:
string::string(const string & s) { length = s.length; str = new char [ length + 1 ]; if ( str == 0 ) { fprintf(stderr, "string::string: could not allocate a string of length %d\n", length); exit(1); } strcpy( str, s.str ); } |
class string { public: string& operator=(const string &); string& operator+=(const string&); string& operator+=(const char*); ... } string& string::operator=(const string &s) // don't do anything if the object to be copied is the same as // the current object if (*this != s) { length = s.length; delete [] str; // free the memory for the old string str = new char [ length + 1 ]; // allocate memory for the new string if ( str == 0 ) { fprintf(stderr, "string::string: could not allocate a string of length %d\n", length); exit(1); } strcpy( str, s.str ); return *this; } } string& operator+=(const string& s) { len += s.len; char *p = new char[len+1]; assert( p != 0); strcpy(p, str); strcat(p, s.str); delete [] str; // free up old memory! str = p; return *this; } string& operator+=(const char *s) { len += strlen(s); char *p = new char[len+1]; assert( p != 0); strcpy(p, str); strcat(p, s); delete [] str; // free up old memory! str = p; return *this; } |
When programmers use operators they typically think of them as commutative. For example, the following two expressions should behave identically:
string x; if (x == "hello") if ("hello" == x)Unfortunately if we define the == operator in the string class only the first expression will work as expected. The second expression will cause a compiler error. The reason is that the compiler tries to call the operator method associated with the object on the left side of the == operator. In other words, the first expression is equivalent to the call x.operator==("hello") while the second expression is equivalent to the call "hello".operator==(x). In the first expression that object is a string object and hence the == operator is defined. In the second expression that object is a char * and hence the == operator is not defined.
To solve this problem C++ allows the programmer to define operators outside of classes. When the programmer does so the programmer declares both of the operator's parameters rather than just one, as is done when the operator is declared in the class. For example:
bool operator==(const string &arg1, const char *arg2) { if (strcmp(arg1.str, arg2) == 0) return true else return false; } bool operator==(const char *arg1, const string &arg2) { if (strcmp(arg1, arg2.str) == 0) return true else return false; }Note that these operator functions are declared outside the class and that they are not prefixed by string::. Also note that they both access the string class's str variable, which is protected. Ordinarily the compiler would protest this invasion of the string class's privacy but the string class can let the compiler know that it is okay for these functions to access the str variable by declaring these two functions as friends:
string class { ... friend bool operator==(const string &arg1, const char *arg2); friend bool operator==(const char *arg1, const string &arg2); };If you want more information about using operator functions as friend functions, check out pp. 213-218 of the Schildt text.