By Stanley B. Lippman , Josée Lajoie , Barbara E. Moo
1-100
2.3.5. Declarations and Definitions
A definition of a variable allocates storage for the variable and may also specify an initial value for the variable. A declaration makes known the type and name of the variable to the program.
extern int i; // declares but does not define i
int i; // declares and defines i
extern double pi = 3.1416; // definition
2.4. const Qualifier
const Objects Are Local to a File By Default
// file_1.cc
int counter; // definition
// file_2.cc
extern int counter; // uses counter from file_1
++counter; // increments counter defined in file_1
Unlike other variables, unless otherwise specified, const variables declared at global scope are local to the file in which the object is defined. The variable exists in that file only and cannot be accessed by other files.
We can make a const object accessible throughout the program by specifying that it is extern :
// file_1.cc
// defines and initializes a const that is accessible to other files
extern const int bufSize = fcn();
// file_2.cc
extern const int bufSize; // uses bufSize from file_1
// uses bufSize defined in file_1
for (int index = 0; index != bufSize; ++index)
// …
2.5. References
A reference must be initialized using an object of the same type as the reference:
int ival = 1024;
int &refVal = ival; // ok: refVal refers to ival
int &refVal2; // error: a reference must be initialized
int &refVal3 = 10; // error: initializer must be an object
A Reference Is an Alias
Because a reference is just another name for the object to which it is bound, all operations on a reference are actually operations on the underlying object to which the reference is bound:
refVal += 2;
adds 2 to ival , the object referred to by refVal . Similarly,
int ii = refVal;
assigns to ii the value currently associated with ival .
When a reference is initialized, it remains bound to that object as long as the reference exists. There is no way to rebind a reference to a different object.
const References
A const reference is a reference that may refer to a const object:
const int ival = 1024;
const int &refVal = ival; // ok: both reference and object are const
int &ref2 = ival; // error: non const reference to a const object
A const reference can be initialized to an object of a different type or to an rvalue (Section 2.3.1 , p. 45 ), such as a literal constant:
int i = 42;
// legal for const references only
const int &r = 42;
const int &r2 = r + i;
The same initializations are not legal for nonconst references. Rather, they result in compile-time errors. The reason is subtle and warrants an explanation.
The same initializations are not legal for nonconst references. Rather, they result in compile-time errors. The reason is subtle and warrants an explanation.
This behavior is easiest to understand when we look at what happens when we bind a reference to an object of a different type. If we write
double dval = 3.14;
const int &ri = dval;
the compiler transforms this code into something like this:
int temp = dval; // create temporary int from the double
const int &ri = temp; // bind ri to that temporary
If ri were not const , then we could assign a new value to ri . Doing so would not change dval but would instead change temp . To the programmer expecting that assignments to ri would change dval , it would appear that the change did not work. Allowing only const references to be bound to values requiring temporaries avoids the problem entirely because a const reference is read-only.
2.8. Class Types
Using the structKeyword
C++ supports a second keyword, struct , that can be used to define class types. The struct keyword is inherited from C.
If we define a class using the class keyword, then any members defined before the first access label are implicitly private ; ifwe usethe struct keyword, then those members are public . Whether we define a class using the class keyword or the struct keyword affects only the default initial access level.
Addison Wesley – c++ faqs (2nd Version)
FAQ 2.04 What are the basics of default parameters?
C++ allows functions to have default parameters. This is useful when a parameter should have a specified value when the caller does not supply a value. For example, suppose that the value 42 should be passed to the function f() when the caller does not supply a value. In that case, it would make the calling code somewhat simpler if this parameter value were supplied as a default value:
void f(int x=42); <-- 1 void f(int x) <-- 2 { //... } int main() { f(29); <-- 3 f(); <-- 4 }
(1) Declare the default parameter(s) in the function declaration
(2) Don’t repeat the default parameter(s) in the function definition
(3) Passes 29 to f()
(4) Passes 42 to f()
FAQ 2.24 What are the basics of inheritance and dynamic binding?
Inheritance is a powerful tool that enables extensibility. It allows the software to capture the is-a or kind-of relationship (although as will be shown in FAQ 7.01, the phrase, "is substitutable for," more accurately captures the true meaning of inheritance).
In the following example, class Vehicle is defined with = 0; after the declaration of the startEngine() member function. This syntax means that the startEngine() member function is pure virtual and the Vehicle class is an abstract base class, or ABC. In practice, this means that Vehicle is an important class from which other classes inherit, and those other derived classes are, in general, required to provide a startEngine() member function.
class Vehicle { public: virtual void startEngine() = 0; virtual ~Vehicle(); <-- 1 }; Vehicle::~Vehicle() { // Intentionally left blank }
(1) Destructors of ABCs are often virtual
The idea with ABCs is to build the bulk of the application so that it knows about the ABCs but not the derived classes. For example, the following function is aware of the ABC Vehicle but is not aware of any of the derived classes.
void f(Vehicle& v) { // ... v.startEngine(); // ... }
If the ABCs are designed properly, a large percentage of the application will be written at that level. Then new derived classes can be added without impacting the bulk of the application. In other words, the goal is to minimize the ripple effect when adding new derived classes. For example, the following derived classes can be added without disturbing function f().
#include <iostream> using namespace std; class Car : public Vehicle { public: virtual void startEngine(); }; void Car::startEngine() { cout << "Starting a Car's engine\n"; } class NuclearSubmarine: public Vehicle { public: virtual void startEngine(); }; void NuclearSubmarine::startEngine() { cout << "Starting a NuclearSubmarine's engine\n"; }
The reason these won’t disturb the code in function f() (and recall, function f() represents the bulk of the application) is because of two features of C++: the is-a conversion and dynamic binding. The is-a conversion says that an object of a derived class, such as an object of class Car, can be passed as a base reference. For example, the following objects c and s can be passed to function f(). Thus the compiler allows a conversion from a derived class (e.g., a Car object) to a base class (e.g., a Vehicle reference).
int main() { Car c; NuclearSubmarine s; f(c); f(s); }
The is-a conversion is always safe because inheritance means "is substitutable for." That is, a Car is substitutable for a Vehicle, so it won’t surprise function f() if v is in fact referring to a Car.
Dynamic binding is the flip side of the same coin. Whereas the is-a conversion safely converts from derived class to base class, dynamic binding safely converts from base class back to derived class. For example, the line v.startEngine() in function f() actually calls the appropriate startEngine() member function associated with the object. That is, when main() passes a NuclearSubmarine into f() (line f(s) in main()), v.startEngine() calls the startEngine() member function associated with class NuclearSubmarine. This is extremely powerful, since class NuclearSubmarine might have been written long after function f() was written and compiled and put into a library. In other words, dynamic binding allows old code (f()) to call new code (NuclearSubmarine::startEngine()) without the old code needing to be modified or even recompiled. This is the essence of extensibility: the ability to add new features to an application without significant impact to existing code. It is doable with C++, but only when the design considerations are carefully thought through ahead of time; it does not come free.