Main

February 04, 2004 (Wednesday)

A brief note about the C++ lectures:

We will be following the C++ textbook, Koenig & Moo (K&M), much more linearly than we did K&R. Also note that most (but not all) of the examples that we'll be using for C++ are directly from K&M. You can find a copy of the text on reserve in the library if you did not buy a copy from the bookstore. You can download all the source code from the text from the book's website. The online notes provide a summary of the material in the book. You should read the book in order to get more detailed descriptions of the concepts presented in these notes.

Hello world! program in C++ (review) (K&M Chapter 0)

We've already seen the C++ version of the Hello world! program on the first day of lectures:

#include	<iostream>

int main()
{
	std::cout << "Hello world!" << std::endl;
	return 0;
}
hello.cpp

Compiling C++ Programs

We can compile the program and generate an executable similar to the method we used earlier for compiling C programs:

$ g++ -o hello -Wall -ansi -pedantic hello.cpp
$ ./hello
Hello world!
$

Better still, we can use a simple Makefile to make compiling C++ programs less tedious:

CXXFLAGS = -g -Wall -ansi -pedantic
Makefile

Notice the Makefile uses CXXFLAGS to pass command line options to the C++ compiler. For C, we used CFLAGS to perform the same function. Unfortunately, most of the machines in the Computer Science department currently have an old version of g++ installed on them (v2.96). This version of the compiler has some limitations with respect to namespaces (described below) and should not be used to compile your C++ programs. Machines in the senior CS lab that currently have a more recent version of g++ installed (v3.x) include bombadil, bombur and boromir. More machines with this version of g++ may become available in the future. When compiling your C++ programs, you must use one of these machines. Note that you can ssh to one of these machines from another machine and do your compilation remotely (make sure that the remote machine is turned on). For example:

faramir$ ssh bombur
bombur$ cd comp3710
bombur$ cat hello.cpp
#include	<iostream>

int
main()
{
	std::cout << "Hello world!" << std::endl;
	return 0;
}
bombur$ cat Makefile
CXXFLAGS = -Wall -ansi -pedantic
bombur$ make hello
g++ -Wall -ansi -pedantic    hello.cpp   -o hello
bombur$ ./hello
Hello world!
bombur$
...
bombur$ exit
faramir$ exit

Some general notes about C++

Introduction to Namespaces

A namespace is simply a collection of related names. The purpose of a namespace is to prevent naming conflicts which can arise in large programs. For example, if libraries supplied by two different vendors both contained a function called func(), then, provided that each vendor placed the function in different namespaces, you can use both versions without having to worrying about naming conflicts. The most common namespace is the standard library and is denoted by the std namespace, as seen in the program above.

To access a type or object inside a namespace, we use the namespace's name in conjunction with the scope resolution operator (::) Therefore, to access the cout object in the std namespace, we write:

std::cout

Note! In g++ v2.96, the standard library names are automatically placed in the global namespace. This is nonstandard behaviour. This bug is fixed in g++ 3.x which, unfortunately, is not yet available on all CS machines. Please use one of the machines mentioned above to compile your C++ programs.

Some C++ books use the using directive in order to import the entire std namespace, as demonstrated by the following program:

#include	<iostream>

using namespace std;

int main()
{
	cout << "Hello world!" << endl;
	return 0;
}
hello3.cpp

This allows us to write cout and endl instead of std::cout and std::endl. While this may seem to be a better way to write our C++ programs, many purists argue that this is not a wise thing to do as it imports all the symbols from the std namespace, thereby causing the pollution of the global namespace. This could result in collisions between your types and objects and the types and objects defined in the std namespace. Therefore, when referencing entities in the standard library such as cout you should either qualify all such references with std:: or, as we'll see later, you can limit the symbols imported from the standard namespace.

Incidentally, the same Makefile given above can be used to compile both the above programs.

cout and the << operator

We've already seen bitwise operators in C and they exist in C++ as well. <<, >>, |, & and ^ are the left shift, right shift, bitwise or, bitwise and and bitwise xor, respectively. C++ overloads the << operator to do output when the first operator is of type std::ostream. Therefore, the statement:

std::cout << "Hello world!" << std::endl;

simply writes the string literal "Hello world!" to the standard output. Note that the << operator is left associative.

(std::cout << "Hello world!") << std::endl;

This means that when applying the << operator to std::cout and a string, the return value is another object of the same type as std::cout (i.e. a std::ostream type). This value is then used as the first operand for the second << operator and the std::endl operand.

Chapter 1: Working with strings

C++ has much better for support for strings than C. However, the core C++ language does not define a native string type per se. Instead, the Standard C++ Library provides a comprehensive string class. For example, consider the following program which inputs a string from the user and displays a greeting for that user:

// ask for a person's name, and greet the person
#include <iostream>
#include <string>

int main()
{
	// ask for the person's name
	std::cout << "Please enter your first name: ";

	// read the name
	std::string name;     // define `name'
	std::cin >> name;     // read into `name'

	// write a greeting
	std::cout << "Hello, " << name  << "!" << std::endl;
	return 0;
}
greet.cpp

This program introduces the standard string type. In order to use strings in C++, we must #include the <string> header. The program also demonstrates how to do input using the std::cin object and the right shift operator (>>).

	std::string name;     // define `name'
	std::cin >> name;     // read into `name'

Note that when inputting a string, the >> function will only input a single word. (i.e. it will skip over any leading whitespace, and read the non-whitespace characters until it hits another whitespace character, at which point it will stop reading). So, for example, if we input the string two words, the output will be Hello two!

Note that unlike C, we no longer have to worry about low-level details such as ensuring we have enough space inside the string to store all the characters. Nor do we have to worry about the nul byte. The string library takes care of all of those details for us! The string literal "Hello, " and the string variable name and a ! are all displayed using std::cout which we saw earlier.

We can make the example a little more complicated by displaying a frame around the greeting as demonstrated by the following program:

// ask for a person's name, and generate a framed greeting
#include <iostream>
#include <string>

int main()
{
	std::cout << "Please enter your first name: ";
	std::string name;
	std::cin >> name;

	// build the message that we intend to write
	const std::string greeting = "Hello, " + name + "!";

	// build the second and fourth lines of the output
	const std::string spaces(greeting.size(), ' ');
	const std::string second = "* " + spaces + " *";

	// build the first and fifth lines of the output
	const std::string first(second.size(), '*');

	// write it all
	std::cout << std::endl;
	std::cout << first << std::endl;
	std::cout << second << std::endl;
	std::cout << "* " << greeting << " *" << std::endl;
	std::cout << second << std::endl;
	std::cout << first << std::endl;

	return 0;
}
frame01.cpp

We can run this program as follows:

$ ./frame01
Please enter your first name: Estragon

********************
*                  *
* Hello, Estragon! *
*                  *
********************
$ 

This program demonstrates several new features:

char * and strings

With respect to strings in C++, note that we can still use char* in C++. However, we cannot treat them the same way as the C++ string library type. Consider the following example:

#include	<iostream>
#include	<string>

int main()
{
	char	*p = "Hello";
	std::cout << p + " world" << std::endl;
	return 0;
}
concat.cpp

In this example, we are trying to concatenate a char* to a string literal. Unfortunately, this does not work as expected. In fact, it doesn't even compile successfully. If we wanted to concatenate them using the + operator, then at least one (or both) of them will have to be converted to a string. We can do this as follows:

	std::cout << std::string(p) + " world" << std::endl;

Now the string concatenation will occur as expected. We could also have done the following:

	std::cout << p + std::string(" world") << std::endl;
	std::cout << std::string(p) + std::string(" world") << std::endl;

We'll learn more about why this works when we talk about type conversions and operator overloading.


Last modified: February 9, 2004 00:41:27 NST (Monday)