Referencing composed and base objects. site:http://zoo.cs.yale.edu/classes/cs427/

C++ Basics

1. Properties of C++

  1. Widely used in the real world.
  2. Close to the machine and capable of producing efficient code.
  3. Gives a programmer fine control over the use of resources.
  4. OOP: Supports the object-oriented programming paradigm.
  5. Modularity: Supports modularity and component isolation.
  6. Correctness: Supports correctness through privacy, modularity, and use of exceptions.
  7. Reusable code: Supports reusabale code through derivation and templates.

2. C++ file types

  1. Header files (.hpp) contain various kinds of declarations needed by clients of a module. They must not contain definitions of non-inline functions.
  2. Implementation files (.cpp) contain definitions of non-inline functions and class methods and a few other kinds of things. Public declarations belong in the corresponding .hpp files.
  3. Object files (.o) contain the code generated by the compiler from one .cpp file.
  4. Static library files (.a) are archives (collections) of object files.
  5. Executable files commands ready to be run. File names have no extension. They must have the “execute” mode bit set. is compiled to produce a corresponding .o file.
  6. Linking: All of the .o files, together with needed code from library files, are combined to produce one executable file.

3. Stages of a program creation. Kinds of errors that can be discovered at each stage. Compilation and linking. What does each do? What files are needed? What files are produced?

Two stages:

  • Compilation: Each .cpp file For every symbol referenced in any of the .o files, a definition must be found in exactly one .o file or in a library.

Compiler errors at each stage:

  • Preprocessor errors: Missing #include files, mismatched #ifndf…#endif pairs, etc.
  • Compilation errors: Syntax, semantics, missing/erroneous declarations. Sometimes the file produced by the preprocessor and seen by the compiler is not what the programmer intended.
  • Linker errors are generally methods that were declared but never defined, often because of mismatched signatures. Duplicate definitions from different modules also detected here, e.g., multiple definitions of main().

4. Qualifiers: inline, const, static

4.1. Static data members - A static class variable must be declared and defined. - A static class member is declared by preceding the member declaration by the qualifier static. - A static class member is defined by having it appear in global context with an initializer but without static. - Must be defined only once.

Example In mypack.hpp file, inside class definition:

    class MyPack {
        static int instances; // count # instantiations

In mypack.cpp file:

    int MyPack::instances = 0;

4.2. Static function members

  • As with static variables, the are declared inside class by prefixing static.
  • They may be defined either inside the class (as inline functions) or outside the class.
  • If defined outside the class, the :: prefix must be used and the word static omitted.

4.3. const declares a variable (L-value) to be readonly.

If they are used at all, global variables should be const. It is not bad style to use a global const variable because you can do things with it that you cannot do with #define (initialize a constant object of a structured type).

Here are three different uses of the const qualifier in a pointer declaration:

        int const* p; //a mutable pointer to a constant int
        int* const p; //a constant pointer to a mutable int
        int const* const r; //a constant pointer to a constant int

A little trick: if you read the declaration statement from right to left, it will be much easier to figure out its meaning. For example:

        int const* p; //p is a pointer to a constant integer
        int* const p; //p is a constant pointer to an integer.

4.4. inline - Methods defined inside a class are inline (e.g., getN()). - Inline functions are recompiled for every call. - Inline avoids function call overhead but results in larger code size. - inline keyword makes following function definition inline. - Inline functions must be defined in the header (.hpp) file. Why? To expand the code of an inline function at the point of call, the compiler must have access to the function definition. The function prototype is insufficient.

5. C++ types: primitive (built-in) value types, class types, enum types, typedef, reference types, and pointer types.

Classes and objects

1. The roles of a class

  • A type from which objects (instances) can be formed. We say the instances belong to the class.
  • A collection of things that belong together; a struct with its associated functions.
  • A way to encapsulate behavior: a public interface with a private implementation.
  • A way to protect the integrity of data, providing the rest of the world with functions that provide a view of the data but no way to modify it.
  • A way to organize and automate allocation, initialization, and deallocation
  • A reusable module.
  • A way to break a complex problem down into manageable, semi-independent pieces, each with a defined interface.
  • An entity that can collaborate with other classes to perform a task

2. Class elements and class structure. class visibility.

2.1. Class elements

  • A class contains declarations for data members and function members (or methods).
  • int n; declares a data member of type int.
  • int getN(){ return n; } is a complete member function definition.
  • void sortData(); declares a member function that must be defined elsewhere.
  • By convention, member names begin with lower case letters and are written in camelCase.

2.2. Visibility

  • The visibility of declared names can be controlled.
  • public: declares that following names are visible outside of the class.
  • private: restricts name visibility to this class.
  • Public names define the interface to the class.
  • Private names are for internal use, like local names in functions.

3. Differences between a class definition and a class implementation. Importance of a class interface.

the public functions of a class are called the class interface.

4. Constructing/destructing objects.

5. Different kinds and roles of constructors: ctors, copy constructors, default constructors.

Whenever a class object is created, one of its constructors is called. If not specified otherwise, the default constructor is called. This is the one that takes no arguments. If you do not define the default constructor, then the null constructor (which does nothing) is used. This applies not only to the “outer” object but also to all of its embedded objects.

5.1. Ctors

(short for constructor/initializors) allow one to supply parameters to implicitly-called constructors.

Example:

    class B : A {  
        B( int n ) : A(n) {};  
        // Calls A constructor with argument n  
    };

5.2. Copy constructors

  • A copy constructor is automatically defined for each new class A and has prototype A(const A&). It initializes a newly created A object by making a shallow copy of its argument.
  • Copy constructors are used for call-by-value parameters.
  • Assignment uses operator=(), which by default copies the data members but does not call the copy constructor.
  • The results of the implicitly-defined assignment and copy constructors are the same, but they can be redefined to be different.

  • Initialization, assignment, copying of objects (and variables in general)

Initialization ctors Ctors also can be used to initialze primitive (non-class) variables.

Example:

    class  B {  
        int x;  
        const int y;  
        B( int n ) : x(n), y(n+1) {}; // Initializes x and y  
    };

Multiple ctors are separated by commas.

Ctors present must be in the same order as the construction takes place – base class ctor first, then data member ctors in the same order as their declarations in the class.

Initialization not same as assignment

Previous example using ctors is not the same as writing

    B( int n ) { y=n+1; x=n; };

The order of initialization differs.

const variables can be initialized but not assgined to.

Initialization uses the constructor (for class objects).

Initialization from another instance of the same type uses the copy constructor.

I/O

1. streams: cin, cout, cerr, and clog

  • cin is the standard input stream.
  • cout is the standard output stream.
  • cerr is the standard output stream for errors.
  • clog is the standard output stream for logging.

Standard output is called cout. Other predefined output streams are cerr and clog. They are usually initialized to standard output but can be redirected.

2. Handling files.

  • ifstream fin ( “myfile.in” ); opens stream fin for reading. This implicitly invokes the constructor ifstream( “myfile.in” ).
  • ifstream fin; creates an input stream not associated with a file. fin.open( “myfile.in” ); attaches it to a file. Can also specify open modes.
  • To test if fin failed to open correctly, write if (!fin) {…}.
  • To close, use fin.close();.

Can read a line into a buffer with fin.get(buf, buflen);. This function stops before the newline is read. To continue, one must move past the newline with a simple fin.get(ch); or fin.ignore();.

3. Manipulators.

Manipulators are objects that can be arguments of >> or << but do not necessarily produce data.

Example:

    cout << hex << x << y << dec << z << endl;

Prints x and y in hex and z in decimal. After printing z, a newline is printed and the output stream is flushed. Manipulators are used in place of C formats to control input and output formatting and conversions.

4. Kinds of I/O errors and ways to handle them.

5. Error flags, their meaning and how they are set.

I/O functions set status flags after each I/O operation. bad means there was a read or write error on the file I/O. fail means the data was not appropriate to the field, e.g., trying to read a non-numeric character into a numeric variable. eof means that the end of file has been reached. good means that the above three bits are all off.

The whole state can be read with one call to rdstate().

    // getting state of stream object
    #include <iostream>
    #include <fstream>
    using namespace std;

    int main () {
        ifstream is;
        is.open ("test.txt");
        if ( (is.rdstate() & ifstream::failbit ) != 0 ) // if failbit flag is on
            cerr << "Error opening 'test.txt'\n";
        return 0;
    }

Functions are also provided for testing useful combinations of status bits.

  • good() returns true if the good bit is set.
  • bad() returns true if the bad bit is set. This is not the same as !good().
  • fail() returns true if the bad bit or the fail bit is set.
  • eof() returns true if the eof bit is set.

As in C, correct end of file and error checking require paying close attention to detail of exactly when these state bits are turned on.

To continue after a bit has been set, must call clear() to clear it.

  1. Extending operators. What operators can be extend and how? Advantages of extending operators.

Operators can be extended to work with new types, for example, complex numbers. +-*/

The built-in arithmetic operators in C and C++ are generic; they are defined for all built-in numeric types. One of the purposes of an object-oriented language is to enable the programmer to define new classes and use them in the same ways that the built-in classes are used. Combining these two facts, we see a need to extend the built-in generic operators to work on new classes. An operator extension implements a built-in function for a new type in such a way that the intention and character of the original operator are preserved.

    inline  
    ostream& operator<<(ostream& out, const Square& sq) {  
        return sq.print(out);  
    }

Functions and methods

1. Passing data to a function. What are the different ways to pass data to a function? Advantages of each method.

  • Call by value: The argument values are copied into the parameter storage locations in the function’s stack frame. There is no way for the function to change values in main’s stack frame.
  • Call by pointer: The arguments must be addresses of integer variables in the caller’s stack frame. These addresses are stored in the parameter locations in the function’s stack frame. They permit the function to modify its caller’s memory locations.
  • Call by reference: The arguments must be integer variables in the caller’s stack frame. The addresses of these variables are stored in the parameter locations in the function’s stack frame, permitting the function to modify its caller’s memory locations.

TODO advantages

2. Receiving data from a function. How data can be passed back from a function? Can more than one value be returned?

  • Return by value.
  • Return by pointer.
  • Return by const T *
  • Return by reference
  • Return by const T &

      int ret0( int* ar ) { return ar[0]; } // Returns an integer R-value.
      int* ret1( int* ar ) { return &ar[1]; } // A pointer R-value.
      const int* ret2( int* ar ) { return &ar[2]; } // A read-only pointer R-value.
      int& ret3( int* ar ) { return ar[3]; } // An L-value (reference).
      const int& ret4( int* ar ) { return ar[4]; } // A read-only L-value reference.
    

Pointers and references

1. L-values and R-values.

Something that can appear on the left is called an L-value. Something that can appear on the right is called an R-value. Intuitively, an L-value is the address of a storage location – some place where a value can be stored. An R-value is a thing that can be placed in a storage location. R-values are sometimes called pure data values.

The declaration int x = 3; says several things:

  • All values that can be stored in x have type int.
  • The name x is bound (when the code is executed) to a storage location adequate to store an int.
  • The int value 3 is initially placed in x’s storage location.

The assignment statement x = 3; means the following:

  • Get an L-value from the left hand side (x).
  • Get an R-value from the right hand side (3).
  • Put the R value from step 2 into the storage location whose address was obtained from step 1.

Given int x = 3; int y = 4; Consider x = y; This is processed as before, except what does it mean to get an R-value from y?

Whenever an L-value is presented and an R-value is needed, automatic deferencing occurs.

This means to go the storage location specified by the presented L-value (y) and get its R-value (4).

Then the assignment takes place as before.

2. References vs. pointers

A pointer [reference value] is a primitive object with an associated L-value.

TODO

3. Relationship between a reference and a pointer.

TODO

Derivation

1. What is derivation?

One class can be derived from another.

Syntax:

    class A {  
    public:  
        int x;  
        ...  
    };  
    class B : public A {  
        int y;  
        ...  
    };

A is the base class; B is the derived class.

B inherits the members from A.

2. When to use and when not to use class derivation.

Use

  • To allow a family of related classes to share common parts.
  • To describe abstract interfaces like Java.
  • To allow generic methods with run-time dispatching.
  • To provide a clean interface between existing, non-modifiable code and added user code.

3. Structure of an object

Structure of an object A simple object is like a struct in C. It consists of a block of storage large enough to contain all of its data members. An object of a derived class contains an instance of the base class followed by the data members of the derived class.

Example:

    class B : A { …};
    B bObj;

Then “inside” of bObj is an A-instance!

4. Referencing composed and base objects.

4.1 Referencing a composed object

Contrast the previous example to

    class B { A aObj; …};
    B bObj;

Here B composes A.

The embedded A object can be referenced using data member name aObj, e.g., bObj.aObj.

4.2 Referencing a base object

    class A { public: int x; int y; …};
    class B : A { int y; …};
    B bObj;
  • The data members of A can be referenced directly by name.
  • x refers to data member x in class A.
  • y refers to data member y in class B.
  • A::y refers to data member y in class A.
  • this points to the whole object. Its type is B*. It can be coerced to type A*.

5. Construction/destruction rules.

5.1 Construction rules

The rule for an object of a simple class is:

  1. Call the constructor/initializer for each data member object in sequence.
  2. Call the constructor for the class.

The rule for an object of a derived class is:

  1. Call the constructor for the base class recursively.
  2. Call the constructor/initializer for each data member object of the derived class in sequence.
  3. Call the constructor for the derived class.

5.2 Destruction rules

When an object is deleted, the destructors are called in the opposite order.

The rule for an object of a derived class is:

  1. Call the destructor for the dervied class.
  2. Call the destructor for each data member object of the derived class in reverse sequence.
  3. Call the destructor for the base class.

Error

1. Five kinds of failures. When different errors can occur?

  • Memory leak—Dynamic storage that is no longer accessible but has not been deallocated.
  • Amnesia—Storage values that mysteriously disapper.
  • Bus error—Program crashes because of an attempt to access non-existant memory.
  • Segmentation fault—Program crashes because of an attempt to access memory not allocated to your process.
  • Waiting for eternity—Program is in a permanent wait state or an infinite loop.

2. Memory leaks – what they are, how they arise, how they can be detected and design patterns for avoiding them.

3. Memory management paradigms.

C++ Standard Library

1. What is the C++ Standard Library and how can it be used?

2. Two classes: stringstream and vector

stringstream – permits I/O to an in-memory string-like object.

vector – creates a growable array of objects of type T, where T can be any type.

Class stringstream

A stringstream object (in the default case) acts like an ostream object.

It can be used just like you would use cout.

The characters go into an internal buffer rather than to a file or device.

The buffer can be retrieved as a string using the str() member function.

stringstream example

Example: Creating a label from an integer.

    #include <sstream>  
    ...  
    int examScore=94;  
    stringstream ss;  
    string label;  
    ss << "Score=" << examScore;  
    label = ss.str();  
    cout << label << endl;
    This prints Score=94.

vector

vector myvec is something like the C array T myvec[].

The element type T can be any primitive, object, or pointer type.

One big difference is that a vector starts empty (in the default case) and it grows as elements are appended to the end.

Useful functions:

  • myvec.push_back( item ) appends item to the end.
  • myvec.size() returns the number of objects in myvec
  • myvec[k] returns the object in myvec with index k (assuming it exists.) Indices run from 0 to size()-1. Other operations on vectors
  • Other operations include creating an empty vector, inserting, deleting, and copying elements, scanning through the vector, and so forth.
  • Liberal use is made of operator definitions to make vectors behave as much like other C++ objects as possible.
  • Vectors implement value semantics, meaning type T objects are copied freely within the vectors.
  • If copying is a problem, store pointers instead. vector examples

You must

    #include <vector>.

Elements can be accessed using standard subscript notion.

Inserting at the beginning or middle of a vector takes time O(n).

Example:

    vector<int> tbl(10);  // creates length 10 vector of int  
    tbl[5] = 7;           // stores 7 in slot #5  
    cout << tbl[5];       // prints 7  
    tbl[10] = 4;          // illegal, but not checked!!!  
    cout << tbl.at(5);    // prints 7  
    tbl.at(10) = 4;       // illegal and throws an exception  
    tbl.push_back(4);     // creates tbl[10] and stores 4  
    cout << tbl.at(10);   // prints 4

Class dependencies

  1. Tightly coupled classes.

Class B depends on class A if B refers to elements declared within class A or to A itself.

A pair of classes A and B are tightly coupled if each depends on the other.

  1. Circular dependencies.

File list.hpp:

        #pragma once  
        #include "cell.hpp"  
        class List { ... };
        File cell.hpp:

        #pragma once  
        #include "list.hpp"  
        class Cell { ... };
        File main.cpp:

        #include "list.hpp"  
        #include "cell.hpp"  
        int main() { ... }

Resolving circular dependencies

  • Suppose the only reference to B in A is to declare a pointer. Then it works to put a “forward” declaration of B at the top of A.hpp, for example:
      class B;
      class A { B* bp; ... };
    
  • If a function defined in A references symbols of B, then the definition of the function must be moved outside the class and placed where it will be read after B has been read in, e.g., in the A.cpp file.
  • If the function needs to be inline, this is still possible, but it’s much trickier getting the inline function definition in the right place.

Visualization

  1. Unified Modeling Language (UML)

TODO

  1. Elementary design principles.

  2. Privacy

  3. Expert
  4. Creation
  5. Deletion
  6. Consistency
  7. Delegation
  8. Don’t talk to strangers
  9. Responsibility