A compound type is a datatype that we define in our code as containing other primitive or compound data types
int
,double
,char
, etc.- Pointers
- A contiguous sequence of objects of the same type
- A compound object made up of member subobjects
- The members and their types are defined by a struct or a class
- The simplest compound type in C and C++ is the struct
- Elements in a struct are laid out in memory in the order listed
double TriangleArea( Triangle *t );
double TriangleArea( Triangle &t );
There are two right ways to access the data inside a struct passed by pointer and one wrong way
double TriangleArea( Triangle *t )
{
( *t ).a + ... // Deref the pointer to get the object
t->a + ... // The most commonly-used notation
*t.a + ... // Does not work, because the dot takes precedence over the dereference
}
Given a pointer, there are two things you can make constant:
- The value of the pointer
- The value of the object the pointer points to
const T *p; // "T" (the pointed-to object)
// cannot be changed
T *const p; // "p" (the pointer) cannot be
// changed
const T *const p; // neither can be changed
Types are composable.
Once you have declared a type struct x, that type can be used to define a pointer to struct x, an array of struct x, or even a struct that contains a struct x.
struct x
{
int i;
char c;
};
struct y
{
x p, *q, r[ 2 ];
};
Procedural abstraction lets us separate what a procedure does from how it is implemented
In C++, we use functions to implement procedural abstraction
//MODIFIES: v
//EFFECTS: sorts v
void sort( std::vector<double> v );
//sort from the standard library
void sort( std::vector<double> &v )
{
std::sort( v.begin( ), v.end( ) );
}
It doesn't matter to the user of the interface how sort()
is implemented, just that it works the way the signature describes
Lets us separate what a type is (and what it can do) from how the type is implemented
- In C-style code, use a
struct
to implement data abstraction - The abstractions we create are called Abstract Data Types (ADTs)
- ADTs let us represent more complex data and more complex relationship between data
- We are no longer limited to
int
,double
, and other primitives - ADTs make programs easier to maintain and modify by partitioning what has to be known
- You can change the implementation adn users of the type should generally not care
Two programmers make a triangle Abstract Data Type
- Alice and Bob agree on an abstraction
Triangle.h
- Alice codes
Triangle.cpp
, implementing the ADT - Bob codes
Graphics.cpp
, using the ADT
ADTs need rules for initialization. For example, a 3, 4, 9 triangle should not be legal, because those side lengths do not form a triangle
We use representation invariants to express the conditions for a valid compound object
void TriangleInit( Triangle*t, double a_in, double b_in, double c_in );
//REQUIRES a_in, b_in, and c_in are greater than 0
// and form a triangle
void TriangleInit( Triangle *t, double a_in, double b_in, double c_in )
{
t->a = a_in;
t->b = b_in;
t->c = c_in;
assert( ( t->a + t->b > t->c ) &&
( t->a + t->c > t->b ) &&
( t->b + t->c > t->a ) );
}
If another programmer uses my code, they should not have to change their code if I change my implementation but not my interface
- Struct definitions go into header (.h) file with comments explaining how they're supposed to be used
- Classes added, bringing language support for public/private, accessors/setters, constructors/destructors, and member functions
Test one piece, e.g., one function, at a time. Find and fix bugs early with smaller, less complex, easier to understand tests
Tets the entire system after all the individually-tested pieces have been integrated
Automatically run all unit and system tests after a code
- Simple
- (Edge) Special
- Stress
istream
ifstream
istringstream
To read input from a stream, use the extraction operator >>
- An input stream that uses a string as its source
- Useful for simulating stream input from a "hardcoded" string
int main( )
{
// A hardcoded PPM image
string input = "P3\n2 2\n255\n255 0 0 0 255 0 \n";
input += "0 0 255 255 255 255 \n";
// Use istringstream for simulated input
istringstream ss_input( input );
Image img;
Image_init( &img, ss_input );
assert( Image_width( &img ) == 2 );
Pixel red = { 255, 0, 0 };
assert( Pixel_equal( Image_get_pixel( &img, 0, 0 ), red ) );
}
ostream
ofstream
ostringstream
To write output into a stream, use the insertion operator <<
- An output stream that writes into a
string
- Useful for capturing output as a
string
that can be checked for correctness
int main( )
{
Matrix mat;
MatrixInit( &mat, 3, 3 );
MatrixFill( &mat, 0 );
MatrixFill_border( &mat, 1 );
// Hardcoded correct output
string output_correct = "3 3\n1 1 1\n1 0 1\n1 1 1\n";
// Capture output in ostringstream
ostringstream ss_output;
Matrix_print( &mat, ss_output );
assert( ss_output.str( ) == output_correct );
}