The intent of this document is to jump-start your learning. The notes simply start the learning process for this course. You will need to learn many more details from the readings, your own experiments, labs, and lecture section discussions.
You are expected to read the materials or their equivalent as each topic comes up in the schedule (q.v.).
Each listed topic has readings and references. Readings have the author name and text reference, plus possible online search strings. After that may be some very brief notes and code examples.
Here are the references that bks has been using:
Eckel 'Thinking In C++' is online in 2 volumes. Links to readings follow the format V-C, where V is the volume number, and C is the chapter. Note that volume 2 is not divided the same way as volume 1. Stroustrup 'Programming Principles and Practice Using C++' Addison-Wesley 2009. Fowler 'UML Distilled' 2ed Addison-Wesley 2000. Stevens 'Wiley's Teach Yourself C++' Wiley 2003. Meyers MEC 'More Effective C++' Addison-Wesley 1996. Meyers EC 'Effective C++' 2ed Addison-Wesley 2003. Koenig 'Accelerated C++' Addison-Wesley 2009. Beck 'Extreme Programming Explained' Addison-Wesley 2000. Riel 'Object-Oriented Design Heuristics' Addison-Wesley 1996.
With Java, you were given a power saw with both a blade guard and a safety-equipped start switch. C++ gives you no guard and no safety switch. Be careful not to cut off your leg!
C and C++ syntax is different from Java, and these differences will surprise and mystify you at first. Handling of memory is not managed by a virtual machine; the programmer is responsible for deciding where and how to allocate memory for objects, and when to de-allocate memory that is no longer needed. Pointer use is all about memory management.
C and C++ syntax is also similar to Java. C and C++ use braces {} to mark block structure, have similar primitive types (e.g. char), collect comma-separated parameters and arguments inside parentheses (), use square brackets [] around array indices, have the same comment notations, and have most of the same conditional and control flow statements (e.g. if, switch, for, while, and do).
READINGS:
Eckel 1-1 search text:
'analysis and design' 'Class-Responsibility-Collaboration (CRC)' 'UML' 'phase' 'extreme programming'
Fowler 1, 2
Riel 1
Eckel emphasizes the importance of analysis and design at an early stage. CRC cards and UML diagramming, especially of the dynamics of software systems, will be useful tools for work on the term project.
Fowler emphasizes the need to deeply analyze the problem following a process so that you don't get frozen into analysis paralysis. This part of his text is a lead-in for using UML as an analysis and design tool. In large scale projects, you really need to have techniques to express the problem AND the possible solution approaches so that you and others can analyze and assess the pros and cons BEFORE code begins.
Riel surveys various design approaches before settling into his OO design heuristics. Though an old text, there is still value in a number of these rules of thumb. The lecture sections will look at heuristics over the quarter.
READINGS:
Eckel 1-2 search text:
'language translation' 'your first c++ program'
Stevens 1 to 6
A C or C++ program has a modular structure. Source code in a single file compiles to a platform-specific object file, or 'translation unit'. The linker program links one or more objects to create a binary, executable file. The simplest programs have only 1 source file, and the compiler program compiles directly from the one source file to a single executable; there are not multiple objects to link.
If you wanted you could compile a single-file program in steps like this on most *nix systems:
g++ -c myprogram.cpp g++ myprogram.o
The first step creates myprogram.o, the object file. The second step reads the object file and creates the binary output file named 'a.out' overwriting any pre-existing file named a.out in the current directory. A more common compiler command to (link and) create an executable specifying the name of the executable. This command creates an executable named myprog in the current directory:
g++ -o myprog myprogram.o
With multiple object modules to compile and then link, there is a need to automate things. Stuart Feldman (now at Google. previously at IBM and Bell Labs) created the 'make' program, an automation tool widely used today.
The file named Makefile (case is sensitive) specifies what can be made by the make program, the order of operations, the rules of operation, and various settings to configure, or tailor, the build process. You create a Makefile to manage your program builds.
Here is an example of a very simple Makefile:
# this is a comment # comments are ignored by make to end of line
myprog: # first target, something to make g++ -o myprog myprogram.cpp # a rule
# end file
The line marked '# a rule' above must start with a TAB character and is a command to execute when someone enters 'make myprog' at the command line. If there are multiple lines of rules, make runs them in order. By default, if a rule fails, make stops executing.
READINGS:
Eckel 1-2 search text:
'header'
Eckel 1-4 search text:
'multiple-declaration problem' 'Importance of header files' 'Header file etiquette' 'Namespaces in headers'
Stevens 11, 13
C++ makes extensive use of libraries that produce a rich programming environment. C++ library names exist in a 'namespace' by the name of 'std', and by default these names are not available without specifying the namespace.
The scope operator '::' connects a scope (such as the namespace std) to a name, so that the programmer can use a single name defined within the scope. If you defined a namespace 'myStuff' and it contained a boolean variable name 'silly', then 'myStuff::silly' would refer to the truth value stored in silly within the myStuff namespace.
To use the libraries, you need to tell your program code the names and signatures of data types, functions, and constants, etc. There are many header files, or '.h files', that you will use to import this information. The compiler's preprocessor knows how to process '#include directives', by which the compiler will read the declarations.
For example, this directive:
#include <climits>
will import the definitions of primitive type size limits such as INT_MIN. When you compile a C++ source file containing the above statement, the preprocessor includes the content of the climits header file into the source text, and the compiler then reads all the declarations so that it can resolve names with their library values.
READINGS:
Eckel 1-2 search text:
'Using the iostreams' 'more about iostreams' 'reading input'
Stevens 28, 29
Stroustrup 2, 3, 7
Basic input in C++ depends on input streams, of which std::cin is the 'standard input' stream defined in the C++ libraries. Input is an 'extraction' of a value from an input stream. To read an integer and store its value, define a variable and use the '>>' operator to 'EXTRACT' the value like this:
int anInt; std::cin >> anInt;
Basic output in C++ depends on output streams, of which std::cout is the 'standard output' stream, and std::cerr is the 'standard error'. Output is an 'insertion' of a value into an output stream. Use the '<<' operator to 'INSERT' the value onto standard output like this:
std::cout << anInt;
While this will do initially, you will need to check for failures after performing extraction and insertion operations. If the data types are incorrect or incompatible, these I/O operations will fail, and the programmer is responsible for verifying correctness and handling the failures.
To use these I/O facilities, you need the following include directive:
#include <iostream>
Here is a source file that shows include directives and simple output:
// File: limits.cpp
#include <climits> #include <iostream>
/// output min and max values
/// of int and char primitive types
/// @author: bks: ben k steele
int main() {
std::cout << "INT_MIN == " << INT_MIN
<< std::endl;
std::cout << "INT_MAX == " << INT_MAX
<< std::endl;
std::cout << "CHAR_MIN == " << CHAR_MIN
<< std::endl;
std::cout << "CHAR_MAX == " << CHAR_MAX
<< std::endl;
return 0;
}
READINGS:
http://www.cppreference.com // an online manual
Eckel 1-2 search text:
'Introducing strings'
Stevens 27 // Standard C++ library
Koenig 5, 6
Stroustrup 23
The std::string class provides character string objects building on top of the primitive char type. For this and other 'standard' C++ classes, you should have a book or keep handy an online link, such as http://www.cppreference.com.
READINGS:
Eckel 1-2 search text:
'Introducing vector'
Stevens 31 // STL Sequence Containers
Koenig 3.2, 5.9
Stroustrup 17
The std::vector< > class manages collections of elements whose type is specified within the <>. A vector is a 'templated class'; the type of the data in the vector is specified in the vector's declaration. The compiler instantiates the vector code incorporating the given type.
A simple example is the definition of a vector of integer elements:
std::vector<int> numList;
This definition associates the numList name with memory whose contents hold integer values. To add a value, you use push_back(); for example:
numList.push_back( 7 );
The collection's post-conditions will be that the collection has 1 more element and that the collection has the value 7 as the last value.
READINGS:
Eckel 1-3 search text:
'introduction to pointers' 'address of an element' 'Arrays are a kind of composite type'
Stevens 8, 9, 17
Koenig 10
Stroustrup 17
A pointer is simply the address of something, or rather a variable whose content MAY BE the address of an element. To get the address of a variable, you apply the address operator '&' to it, like this:
std::cout << &someVariable ;
Typically you define a pointer variable to hold an address, like this:
int *numPointer( 0 );
You should initialize the pointer to 0 to give it an invalid 'null' address. (That assumes you will later set it to something valid.)
An array is a contiguous sequence of elements in memory. Access to array elements is commonly through its name plus a zero-based index. Attempts to access with an out-of-range index will produce unpredictable results; there is no safety net.
READINGS:
Eckel 1-3 search text:
'Introduction to C++ references'
Stevens 3, 9
Unlike pointers, references cannot be null; they must refer always to valid elements. Think of references as aliases for other variables.
READINGS:
Eckel 1-3 search text:
'composite type creation' 'typedef' 'Combining variables with struct' 'Pointers and structs' 'Saving memory with union'
Stevens 5, 6
The C struct is the pre-cursor of the C++ class. A union allows you to define one block of storage that can hold several different types of values, and only one value is valid at a point in time.
READINGS:
Eckel 1-8 // const chapter
'const in classes'
Meyers EC Items 1, 21 http://www.aristeia.com // Meyers web page
You should focus on the C++ usage of const. Proper use enables the compiler to guarantee consistency in your program by preventing accidental changes at compile time. As Meyers says, 'use const whenever possible'.
A const element is one that cannot change or that promises that it will not change its owner (here owner would be a class instance).
Rather than use a #define macro, use a const to replace it. If a method will never change its instance, make that function const. If a method will never change its arguments, make the parameters const.
READINGS:
Eckel 1-4 search text:
'The basic object' 'What's an object?' // structs vs. classes 'Global scope resolution'
Eckel 1-5 search text:
'access control' 'friends'
Stevens 14, 15, 16
Koenig 9, 11
Stroustrup 9
The class construct of C++ grew out of the struct construct of C. Eckel's text has a lot of historical material on this in volume 1 chapter 4. Eckel shows somewhat of an evolution from using a struct to compose variables (fields) and functions (methods) into a unit to using a class. The key difference is that all members of a struct are always public; there is no struct access control. In contrast, the class construct allows you to specify public/protected/private access for clients.
Like Java, constructors are responsible for initializing a new instance to have known, correct values.
Destructors are functions that define how to dispose of (delete) an object when it is no longer needed. By default the compiler will create a destructor that will do the simplest cleanup. If the class has dynamically allocated member fields, you must write your own destructor to dispose of the object's elements properly. Defining a destructor properly involves taking full responsibility for memory management of class instances.
A friend of a class is another class or function that has access to private parts of the class. If class Bletch declares function foobar() to be a friend and foobar's parameter is a Bletch&, then the body of foobar can access all private members of the Bletch instance. While friendship breaks encapsulation, it enables external functions (such as extraction operations) to access hidden parts in a controlled fashion.
READINGS:
Eckel 1-10
Namespaces partition the universe of names into segments to minimize collisions. The static keyword has several uses, one of which is to keep a name and its storage local (and private) to the module in which it appears. This hides the name and reduces the size of the outer, global name list.
READINGS:
Eckel 1-11 search text: copy constructors
'Pointers in C++' 'References in C++' 'Argument-passing guidelines' 'Bitcopy versus initialization' 'Alternatives to copy-construction'
Koenig 11.3 // copy control
Stroustrup 18
A copy constructor is a special kind of constructor that will create a copy of its argument. By default, the compiler creates this function. If your class uses dynamic memory, you must either define appropriate copy constructor behavior for the class or declare the copy constructor private to prohibit client access and stop the compiler from creating one.
When writing a copy constructor for a user defined type (class), you almost always need to overload 'operator=', the assignment operator.
READINGS:
Eckel 1-12 search text:
'Overloadable operators' 'Return by value as const' 'return optimization' 'operator->' 'overloading assignment' 'keyword explicit'
Stevens 21
Meyers MEC Items 5, 28, 29 // reference counting
Koenig 11
Stroustrup 9.6
Though called 'syntactic sugar', Operator overloading is an important feature of C++ because template libraries make extensive use of overloading for generic collections.
Overloading is also essential to define I/O extraction and insertion operators (functions) for user defined types.
For reference counting, a memory management technique, you need to overload operators to perform pointer dereferencing, among other things.
READINGS:
http://www.gotapi.com/ccpp // an online manual
Eckel 1-16 search texts:
'large preprocessor macros' 'parameterized type' 'substitution' 'template definitions are special' 'weak typing' 'you must consider ownership' 'Introducing iterators'
Eckel 2-5, 2-6, 2-7
Stevens 25, 26, 27, 30 to 34
Koenig 5, 6, 7, 8
Stroustrup 17, 18, 19, 20, 21
While templates are a late topic in many texts, they are so important that you will see them starting in week 1, with vector.
The second volume of Eckel goes into great depth.
READINGS:
Eckel 1-14 search text:
'Inheritance syntax'
Eckel 1-15 search text:
'polymorphism' 'function call binding' 'dynamic binding' 'virtual functions' 'picturing virtual functions' 'why virtual functions'
Stevens 23, 24
Koenig 13
Stroustrup 14
The syntax for creating subclasses is simple:
class Derived : Base { // D. inherits from B.
The words used to describe inheritance are different from Java. 'Subclassing' is to Java as 'deriving' is to C++. In C++ you DERIVE a new class from an existing 'base class', and it is called a 'derived class'.
Another twist with C++ inheritance is that members of the base class are private by default in the derived class. Clients of the derived class cannot access the base class components. To make the public parts of the base class accessible by clients, you must make the inheritance public.
class Derived : public Base {
By default, class methods are not late-binding as in Java. That means if you want polymorphism, you must specify it with the keyword 'virtual'. There is a ton more learning to do on classes and inheritance.
READINGS:
Eckel 1-13 search text: dynamic object creation
'operator new' 'operator delete' 'Cleanup responsibility with pointers' 'new & delete for arrays' 'ownership'
Stevens 20
Koenig 10, 14
Stroustrup A.5.6
Eckel starts with C's approach to the heap, but you should skip that because it is for C ONLY. Java took its new from C++, but delete is hidden inside Java's virtual machine garbage collector.
The programmer, that's you, are the garbage person in C++; you are responsible for throwing out things when they are no longer needed. Since this is a hard job, you need tools to track memory during development; debuggers provide one kind of tool to track memory allocation. There are other, dedicated tools as well.
The twist for delete is to know when you need to 'delete [] something;' or 'delete something;'. The first statement deletes an aggregate, and the second deletes a single object. If that something is a collection (aggregate), the standard delete will leak memory. If that something is not a collection, the 'delete []' syntax will cause problems with the memory allocation library.
READINGS:
Eckel 1-2 search text:
'reading input'
Eckel 1-12 search text:
'Non-member operators' '<< and >> are overloaded for iostreams'
Stevens 2, 28, 29
Koenig 12.3
Stroustrup 10, 11
While you see the input and output operators (extraction and insertion) early, the details for writing your own I/O operators for user-defined classes comes later. The operator>> and operator<< functions can be overloaded to apply to user-defined classes so that code can use iostreams easily.
READINGS:
Eckel 1-2 search text:
'Reading and writing files'
Eckel 2-4 // iostreams
'why iostreams?' 'Handling stream errors' 'manipulators' 'Iostream examples'
Stevens 27, 28, 29
Koenig B.1
Stroustrup 10, 11
The basics of file I/O comes early in Eckel, but there is little error checking, if any. Stream formatting, error recovery and other topics will be covered with various examples.
READINGS:
Eckel 2-1 search text:
'Exception Handling' 'throwing an exception' 'catching an exception'
Stevens 35
Stroustrup 5.6
Since Java took its exception handling approach from C++, the mechanics should be familiar, with the twist that C++ does not require an exception object to be an instance of a class.
NOTE: The 13 March 2010 final draft of the C++0x Language Standard officially deprecates the use of exception specifications for functions in C++. While this syntax is available, you should refrain from adding throw specifications to function signatures.
READINGS:
Eckel 1-1 search text:
'Write tests first'
Eckel 1-3 search text:
'assert( ) macro'
Beck 8, 9, 11, 18
Stroustrup 5.11, 26
Extreme programming advocates writing tests first, before writing the code! These tests specify the inputs and the expected outputs in detail. When all the tests pass, the program is correct. The idea is that, if you can specify how to use the program and what the results should be, then you really know what to do.
The assert macro is a tool for ensuring the truth of pre-conditions, post-conditions, and invariant conditions in the code. When a condition fails, assert will cause the program to abort with a core dump. Then you must analyze the failure by analyzing the core dump with a debugger.
Style is not as uniform in C and C++ as it is with Java. There are many styles because C is an old language and C++ evolved from C.
The labs in this course display several variations in style. When you write your solutions using supplied code, you should be a 'chameleon coder'. That means you should maintain the existing style and conform to its rules so that the evolved end product has a uniform look and feel.
Resources:
touch-header // shell script touch-cpp // shell script
For projects, you must start from scratch, and you must choose and consistently use some style.
In bks lecture sections, the preferred style is known as 'triple-slash', named after the language token that starts a published code comment. If you have seen C# code, you should be familiar with this approach.
It's handy to have a tool to create a skeleton file for writing NEW programs. Guess what? There touch-header script creates an appropriately formatted .h file, and the touch-cpp script creates an appropriately formatted .cpp file,
What you should do is save these scripts to your bin directory. Then you may customize them to tailor (fine-tune) their output. (This will give you practice with shell scripts.)
If you prefer so-called javadoc style over triple-slash, that is permissible also. In this case, you should modify the touch scripts to produce a javadoc style skeleton.