This page contains C and C++ programming samples written by me, web site author Chris Berger. The purpose of this portfolio is to demonstrate my coding style and proficiency with C and C++.
Pointers
// This function returns the number of elements in a string
#include <stddef.h> // "header" file that defines size_t type
size_t MyStrlen(const char *s1) // declare function myStrlen
{
const char *returnAddress = s1; // declare pointer to first element of s1
while (*s1++) // use while loop to simply increment pointer
{
if (!*s1) // if null character is encountered...
{
return (size_t)(s1 - returnAddress); // return the length of s1
}
}
return 0; // this return statement should never be executed
} // end function myStrlen
// This function lexigraphically compares two strings
int MyStrcmp(const char *s1, const char *s2)
{
do
{
if (*s1 > *s2) // if the char in s1 is greater than the char in s2
{
return 1; // s1 is lexigraphically greater than s2, return 1
}
else if (*s1 < *s2) // if the char in s1 is less than the char in s2
{
return -1; // s1 is lexigraphically less than s2, return -1
}
else // s1 is lexigraphically equal to s2, need to evalute more
continue;
} while ((*s1++ != '\0') && (*s2++ != '\0')); // increment the char to evaluate unless '\0' is found
// if the loop exists without returning a value, that means the strings were the same until '\0' was
// encountered in both, which means the strings are lexigraphically equal, return 0
return 0;
}
// This function returns a substring from a given string
char *GetSubstring(const char source[], int start, int count, char result[])
{
char *resultAddress = result;
do
{
if ((*source == '\0') || start == 0) // if string has ended or substring has started
break;
} while (*source++ && start--); // otherwise advance one char in string, decrement start
while ((*source != '\0') && count > 0) // while string hasnt ended nor substring ended
{
*result++ = *source++; // store character of string into substring
count--; // advance substring counter
}
*result = '\0'; // store null character as final character in substring
return resultAddress; // this return statement should never be executed
}
// This program converts a string to all upper case
#include <stddef.h>
#include <ctype.h>
size_t StrToUpper(char *newString, const char *origString)
{
const char *newStringStart = newString;
while (*newString++ = toupper(*origString++));
return((newString - newStringStart) -1);
}
// This function uses strtok() to delimit strings from a file and display each delimited piece on a new line
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// No line in the file will ever exceed this size
#define MAX_LINE_SIZE 255
// These are the delimiters used to parse the string
#define DELIMITERS "AEIOUaeiou\t\n"
void ParseStringFields(FILE *fp)
{
char line[MAX_LINE_SIZE];
// while fgets doesn't return a NULL, get the next line from the file
while (fgets(line, MAX_LINE_SIZE, fp))
{
// for each delimited piece of the line, print the piece of the line to the console
// start on the first non-whitespace character in the string
for (char *cp = line; cp = strtok(cp, DELIMITERS); cp = NULL)
{
// if the char pointed to by cp is a whitespace, increment cp to the next char
// this will result in the delimited string not starting with any whitespace
while (isspace(*cp))
++cp;
// print the delimited string
puts(cp);
}
}
}
// This file declares a 4-dimensional pointer array of type float
// the dimensions (left-to-right) of the pointer array
extern const int DIM0 = 2, DIM1 = 3, DIM2 = 4, DIM3 = 5;
// these DIM0*DIM1*DIM2 arrays, each containing DIM3 floats, contain the individual elements of the array
static float w1[DIM3], w2[DIM3], w3[DIM3], w4[DIM3], w5[DIM3], w6[DIM3], w7[DIM3], w8[DIM3], w9[DIM3],
w10[DIM3], w11[DIM3], w12[DIM3], w13[DIM3], w14[DIM3], w15[DIM3], w16[DIM3], w17[DIM3], w18[DIM3], w19[DIM3],
w20[DIM3], w21[DIM3], w22[DIM3], w23[DIM3], w24[DIM3];
// these DIM0*DIM1 arrays, each containing DIM2 pointers to floats, point to the rows of the array
static float *x1[DIM2] = {w1, w2, w3, w4}, *x2[DIM2] = {w5, w6, w7, w8}, *x3[DIM2] = {w9, w10, w11, w12},
*x4[DIM2] = {w13, w14, w15, w16}, *x5[DIM2] = {w17, w18, w19, w20,}, *x6[DIM2] = {w21, w22, w23, w24};
// these DIM0 arrays, each containing DIM1 pointers to pointers to floats, point to the pages of the array
// I have to use y_1 and y_2 instead of y1 and y2 because y1 is also a built-in function in math.h
static float **y_1[DIM1] = {x1, x2, x3}, **y_2[DIM1] = {x4, x5, x6};
// this pointer to pointer to pointer to floats points to the books (if you will) of the array
float ***pointerArray4D[DIM0] = {y_1, y_2};
// This program outputs the location of the first instance of a given value in an array
#include <stddef.h>
int *FindFirstInt(const int *ptr, size_t count, int value)
{
for (unsigned int ptrIndex = 0; ptrIndex < count; ++ptrIndex)
{
if (*(ptr + ptrIndex) == value)
{
return(ptr + ptrIndex);
}
}
return(NULL);
}
// This function computes the average element value for a 4-D array using two methods. The first method
// accesses the elements of the array using 4 nested for loops and 4-D indexing. The second method
// accesses the elements of the array using linear compact pointer notation.
#include "C2A4E1_ArraySize.h"
void ComputeAverages(float (*anArray)[DIM1][DIM2][DIM3], float *nestedAvg, float *linearAvg)
{
float subTotal = 0;
// access the elements using 4 nested for loops and 4-d indexing
for (int index0 = 0; index0 < DIM0; ++index0)
for (int index1 = 0; index1 < DIM1; ++index1)
for (int index2 = 0; index2 < DIM2; ++index2)
for (int index3 = 0; index3 < DIM3; ++index3)
subTotal += anArray[index0][index1][index2][index3];
*nestedAvg = subTotal / Elements4D;
subTotal = 0;
// access the elements using compact pointer notation
for (float *ptr = (float *)anArray; ptr < (float *)anArray + DIM0 * DIM1 * DIM2 * DIM3; ++ptr)
subTotal += *ptr;
*linearAvg = subTotal / Elements4D;
}
// This function sorts values in an array in descending order using a bubble sort algorithm
#include <cstdlib>
float *SortValues(float *first, size_t elements)
{
// store the location of the last element array
float *last = &first[elements - 1];
// the bubble sort algorithm
bool didSwap;
do
{
didSwap = false;
// for each non-bubbled element of the array
for (float *current = first, *nextCurrent = current + 1; current < last; ++current, ++nextCurrent)
{
// if the current array value is less than the next array value, swap them
if (*current < *nextCurrent)
{
float temp = *current;
*current = *nextCurrent;
*nextCurrent = temp;
didSwap = true;
}
}
// don't check the bubbled value
--last;
} while (didSwap);
// return the location of the start of the sorted array
return first;
}
Memory Handling
// This function swaps the values of two objects given two pointers pointing to the two objects
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void SwapObjects(void *pa, void *pb, size_t size)
{
// declare a temporary pointer that will be used for the object swap
void *vp;
// allocate enough bytes to hold one object, for temporary storage (exit program if unable teo allocate)
if ((vp = malloc(size)) == NULL)
{
fputs("Could not allocate memory\n", stderr);
exit(EXIT_FAILURE);
}
// copy the object stored at *pb to *vp
memcpy(vp, pb, size);
// copy the object stored at *pa to *pb
memcpy(pb, pa, size);
// copy the object stored at *vp to *pa. this completes the swap
memcpy(pa, vp, size);
// free the temporary storage
free(vp);
}
// This file contains the following functions:
// Create2D() creates a 2-dimensional pointer array of data type Type having "rows" rows and "cols" columns.
// Free2d() frees memory pointed to by its argument
// SafeMalloc() attempts to allocate memory, and outputs an error to stderr and exits if allocation fails
#include <stdio.h>
#include <stdlib.h>
#include "C2A5E2_Type-Driver.h"
static void *SafeMalloc(size_t memSize)
{
void *vp;
// Attempt to allocate memory. If allocation fails, output error to stderr and exit
if ((vp = malloc(memSize)) == NULL)
{
fputs("Could not allocate memory\n", stderr);
exit(EXIT_FAILURE);
}
// Return pointer to allocated memory
return(vp);
}
Type **Create2D(size_t rows, size_t cols)
{
Type **beginPtr, **currentRow, *nextRow, **lastRow;
// allocate enough memory to contain the entire array
beginPtr = (Type **)SafeMalloc(rows * sizeof(Type *) + rows * cols * sizeof(Type));
// compute the address of the location of the last row
lastRow = beginPtr + rows;
nextRow = (Type *)lastRow;
// for each row, store the address of each row in the Type* array
for (currentRow = beginPtr; currentRow < lastRow; ++currentRow, nextRow += cols)
{
*currentRow = nextRow;
}
// return a pointer to the start of the array
return(beginPtr);
}
void Free2D(void *p)
{
// free memory pointed to by p
free(p);
}
// This function dynamically allocates a block of memory containing newSize bytes and
// resizes an existing block containing oldSize bytes to contain newSize bytes. All existing data
// from pOld that will fit into newSize bytes will be preserved.
#include <stdlib.h>
void *ResizeAlloc(void *pOld, size_t newSize, size_t oldSize)
{
// if new memory is 0 bytes in size, return null pointer and quit
if (newSize == 0)
{
return(NULL);
}
// otherwise, if new memory is greater than 0 bytes
else
{
// allocate new block of memory newSize bytes in size. if allocation fails, return
// null pointer and quit
unsigned char *newAllocatedPointer;
if ((newAllocatedPointer = (unsigned char *)malloc(newSize * sizeof(unsigned char))) == NULL)
{
return(NULL);
}
// otherwise if pOld is a null pointer, just return a pointer to the newly allocated memory
else if (pOld == NULL)
{
return(newAllocatedPointer);
}
//otherwise, copy data from the pOld into the newly allocated memory
else
{
unsigned char *newAllocatedPointerStart = newAllocatedPointer, *pOldChar = pOld;
if (newSize > oldSize)
{
for (*newAllocatedPointer = *pOldChar;
(size_t)(newAllocatedPointer - newAllocatedPointerStart) < oldSize;)
{
*newAllocatedPointer++ = *pOldChar++;
}
}
else
{
for (*newAllocatedPointer = *pOldChar;
(size_t)(newAllocatedPointer - newAllocatedPointerStart) < newSize;)
{
*newAllocatedPointer++ = *pOldChar++;
}
}
free(pOld);
// return the location of the newly allocated memory
return(newAllocatedPointerStart);
}
}
}
Function Pointers
// This file contains the following functions:
// GetPrintfPointer() returns a pointer to the C library function printf()
// GetPutsPointer() returns a pointer to the C library function puts()
#include <stdio.h>
int (*GetPrintfPointer(void))(const char *format, ...)
{
// initialize a pointer to function returning int and set to function printf()
int (*ptrToPrintf)(const char *format, ...) = printf;
return(ptrToPrintf);
}
int (*GetPutsPointer(void))(const char *str)
{
// initialize a pointer to function returning int and set to function puts()
int (*ptrToPuts)(const char *str) = puts;
return(ptrToPuts);
}
// This file contains the following three functions:
// Compare() - uses C library function strcmp() to compare two strings from a ragged string array
// SortStudents() - uses qsort() to sort an array of strings using Compare()
// DisplayClassStatus() - uses bsearch() to find attendees who weren't registered, and registered students
// who did not attend, and prints that list.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int Compare(const void *elemA, const void *elemB)
{
// use strcmp() to determine whether elemA is greater than, less than, or equal to elemB
return(strcmp(*(char **)elemA, *(char **)elemB));
}
void SortStudents(const char *studentList[], size_t studentCount)
{
// use qsort() to sort the student list using Compare()
qsort((void *)studentList, studentCount, sizeof(studentList[0]), Compare);
}
void DisplayClassStatus(const char *registrants[], size_t registrantCount,
const char *attendees[], size_t attendeeCount)
{
printf("Not present:\n");
// for each registrant
for (int registrant = 0; registrant < (int)registrantCount; registrant++)
{
// find whether the registrant attended
char *match = (char *)bsearch(®istrants[registrant], attendees,
attendeeCount, sizeof(attendees[0]), Compare);
// if the registrant didn't attend
if (match == NULL)
{
// print the name of the non-attending registrant
printf("%s\n", registrants[registrant]);
}
}
printf("Not registered:\n");
// for each attendant
for (int attendant = 0; attendant < (int)attendeeCount; attendant++)
{
// find whether the attendant was registered
char *match = (char *)bsearch(&attendees[attendant], registrants,
registrantCount, sizeof(registrants[0]), Compare);
// if the attendant was not registered
if (match == NULL)
{
// print hte name of the non-registered attendant
printf("%s\n", attendees[attendant]);
}
}
}
Bit Operations
// This function finds the number of bits used in an unsigned int type
int CountIntBitsF(void)
{
//set an unsigned int equal to 1, initialize the bit counter
unsigned int intTest = 1;
int numBitsCounter;
// perform left bit shift on the unsigned int. when the unsigned int is equal to 0, we are done
// increment the bit counter for each left bit shift performed on the unsigned int
for (numBitsCounter = 1; intTest <<= 1; ++numBitsCounter)
;
// return the bit counter value
return numBitsCounter;
}
// This function performs a "count"-length bitwise circular shift on unsigned object "object"
int CountIntBitsF();
unsigned Rotate(unsigned object, int count)
{
// if count is negative, perform a left circular shift
if (count <= 0)
{
// left shift object count bits
unsigned firstShift = object << -count % CountIntBitsF();
// right shift object count bits less than the number of bits in an unsigned
unsigned secondShift = object >> (CountIntBitsF() - (-count % CountIntBitsF()));
// bitwise-ORing the left and right shifts gives the final left circular shift
return(firstShift | secondShift);
}
// if count is positive, perform a right circular shift
else
{
// right shift object count bits
unsigned firstShift = object >> count % CountIntBitsF();
// left shift object count bits less than the number of bits in an unsigned
unsigned secondShift = object << (CountIntBitsF() - (count % CountIntBitsF()));
// bitwise-ORing the left and right shifts gives the final right circular shift
return(firstShift | secondShift);
}
}
File Handling
// This function opens count files specified by fileNames
#include <cstdlib>
#include <fstream>
#include <iostream>
using namespace std;
ifstream *OpenFiles(char * const fileNames[], size_t count)
{
// if the number of files specified by count is zero, output an error message and exit
if (!count)
{
cerr << "No files were specified\n";
exit(EXIT_FAILURE);
}
// dynamically allocate memory to store the information for each file to open
// if allocation fails, output an error message and exit
ifstream *files;
if ((files = new (nothrow) ifstream[count]) == NULL)
{
cerr << "Unable to allocate memory\n";
exit(EXIT_FAILURE);
}
// for each file to open...
for (int currentFile = 0; currentFile < (int)count; ++currentFile)
{
// open the file
files[currentFile].open(fileNames[currentFile]);
// if the file fails to open...
if (!files[currentFile].is_open())
{
// output an error message describing which file didn't open
cerr << "\"" << fileNames[currentFile] << "\": Failed to open\n";
// for each file that successfully opened before the file that failed to open...
for (; currentFile >= 0; --currentFile)
{
// close the file
files[currentFile].close();
}
// delete the file information allocated memory
delete files;
exit(EXIT_FAILURE);
}
}
return files;
}
// This function appends data from one file to the end of another file
#include <stdio.h>
// AppendFile opens the file described by outFile and appends the file described by inFile to the end of it
int AppendFile(const char *inFile, const char *outFile)
{
FILE *fileToBeAppendedTo, *appendData;
// open the file to be appended to in write-only append mode
// open the file containing the data to append in read-only mode
if ((fileToBeAppendedTo = fopen(outFile, "ab")) == NULL)
{
// if file doesn't open for some reason, print an error message and quit
fprintf(stderr, "***ERROR*** AppendFile: Could not open outFile\n\n");
return -1;
}
else if ((appendData = fopen(inFile, "rb")) == NULL)
{
// if file doesn't open for some reason, print an error message and quit
fprintf(stderr, "***ERROR*** AppendFile: Could not open inFile\n\n");
fclose(fileToBeAppendedTo);
return -1;
}
else
{
// execute loop until a break statement is encountered
for (;;)
{
// get the next byte of data from the inFile
int valToAppend = fgetc(appendData);
// if the next byte of data is an EOF, break out of for loop
if (valToAppend == EOF)
{
break;
}
// append the byte of data from inFile to outFile
fputc(valToAppend, fileToBeAppendedTo);
}
// close both files and return success
fclose(fileToBeAppendedTo);
fclose(appendData);
return 0;
}
}
// This function appends data from one file to the end of another file
#include <iostream>
#include <fstream>
using namespace std;
// AppendFile opens the file described by outFile and appends the file described by inFile to the end of it
int AppendFile(const char *inFile, const char *outFile)
{
// open the file to be appended to in write-only append mode
ofstream fileToBeAppendedTo(outFile, ios_base::app);
// open the file containing the data to append in read-only mode
ifstream appendData(inFile);
// if either file doesn't open for some reason, print an error message and quit
if (!fileToBeAppendedTo.is_open() || !appendData.is_open())
{
cerr << "Could not open a file\n";
return -1;
}
else
{
// execute loop until a break statement is encountered
for (;;)
{
// get the next byte of data from the inFile
char valToAppend;
appendData.get(valToAppend);
// if the next byte of data is an EOF, break out of for loop
if (appendData.eof())
{
break;
}
// append the byte of data from inFile to outFile
fileToBeAppendedTo.put(valToAppend);
}
// close both files and return success
fileToBeAppendedTo.close();
appendData.close();
return 0;
}
}
// This function opens, merges, and displays the contents of multiple files
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <cstring>
using namespace std;
// maximum line size
const int BUFFER = 512;
void MergeAndDisplay(ifstream files[], size_t count)
{
// declare a boolean indicating whether at least one file is still open to read lines from
bool atLeastOneFileOpen = true;
// declare a char array to store the text from one line
char temp[BUFFER];
// declare a boolean indicating whether a new line was the last text output
bool newLineWasLastOutput = true;
// while at least one file is open (i.e. while not all files are closed)
while (atLeastOneFileOpen)
{
// begin with the assumption that no files are open
// if it turns out a file is in fact open, this boolean will get set to true
atLeastOneFileOpen = false;
// for each file
for (int currentFile = 0; currentFile < (int)count; ++currentFile)
{
// if the file is open
if (files[currentFile].is_open())
{
// get the next line from the file
files[currentFile].getline(temp, sizeof(temp));
// if an EOF was not encountered in the file
if (!files[currentFile].eof())
{
// if the line that was read in from the file contains only a new line character
if (!strlen(temp))
{
// print out a new line character
cout << "\n";
// indicate that the last thing output was a newline
newLineWasLastOutput = true;
}
// if a new line was not the last thing output to the console
if (!newLineWasLastOutput)
{
// output a new line
cout << "\n";
}
// output the line from the file
cout << temp;
// indicate that the last thing output was not a newline
newLineWasLastOutput = false;
// indicate that at least one file is still open
atLeastOneFileOpen = true;
}
// if an EOF was encountered in the file
else
{
// close the file
files[currentFile].close();
}
}
}
}
}
Headers and Inline Functions
// This header file includes two inline functions that return the maximum of 2 or 3 numbers
// and two function-like macros that return the maximum of 2 or 3 numbers
// include guard
#ifndef C1A4E4_MAXOF_H
// begin definition of C1A4E4_MAXOF_H
#define C1A4E4_MAXOF_H
//declare inline function fMaxOf2 which returns the maximum of 2 numbers
inline long double fMaxOf2(long double firstNum, long double secondNum)
{
// return the maximum of two numbers
return((firstNum > secondNum) ? firstNum : secondNum);
}
// declare inline function fMaxOf3 which returns the maximum of 3 numbers
inline long double fMaxOf3(long double firstNum, long double secondNum, long double thirdNum)
{
// return the maximum of three numbers
return fMaxOf2(firstNum, fMaxOf2(secondNum, thirdNum));
}
// declare function-like macro mMaxOf2 which returns the maximum of 2 numbers
#define mMaxOf2(firstNum, secondNum) ((firstNum) > (secondNum) ? (firstNum) : (secondNum))
// declare function-like macro mMaxOf3 which returns the maximum of 3 numbers
#define mMaxOf3(firstNum, secondNum, thirdNum) (mMaxOf2((firstNum), mMaxOf2((secondNum), (thirdNum))))
// end of include guard
#endif
Class Example: Savings Account
#ifndef CHRISBERGER_SAVINGSACCOUNT_H
#define CHRISBERGER_SAVINGSACCOUNT_H
namespace ChrisBerger
{
class SavingsAccount
{
public:
// ptototype for constructor taking one double argument
SavingsAccount(double initialBalance);
// getter for savingsBalance
double getSavingsBalance() const;
// setter for annualInterestRate
void applyMonthlyInterest();
// member function to apply one month of interest to savingsBalance
static void setAnnualInterestRate(double newInterestRate);
private:
// member attributes
double savingsBalance;
static double annualInterestRate;
};
}
#endif // !CHRISBERGER_SAVINGSACCOUNT_H
#include "SavingsAccount.h"
#include <iostream>
using std::cerr;
// initialize static variable annualInterestRate
double ChrisBerger::SavingsAccount::annualInterestRate;
// constructor taking one argument for the initial balance
ChrisBerger::SavingsAccount::SavingsAccount(double initialBalance)
{
// if the initial balance is negative, display an error and set it to 0
if (initialBalance < 0)
{
cerr << "Initial balance must be nonnegative; setting balance to 0\n";
savingsBalance = 0;
}
// otherwise, initialize the balance
else
{
savingsBalance = initialBalance;
}
}
// getter for savingsBalance
double ChrisBerger::SavingsAccount::getSavingsBalance() const
{
return savingsBalance;
}
// setter for annualInterestRate
void ChrisBerger::SavingsAccount::setAnnualInterestRate(double newInterestRate)
{
// if the rate is negative, display an error and set the rate to 0
if (newInterestRate < 0)
{
cerr << "New interest rate must be nonnegative; setting rate to 0\n";
ChrisBerger::SavingsAccount::annualInterestRate = 0;
}
// otherwise, set the rate
else
{
ChrisBerger::SavingsAccount::annualInterestRate = newInterestRate;
}
}
// member function to accure interest on the savingsBalance
void ChrisBerger::SavingsAccount::applyMonthlyInterest()
{
// add one month of interest to the account balance
savingsBalance += savingsBalance *
ChrisBerger::SavingsAccount::annualInterestRate / 12;
}
Operator Overloading: Complex Numbers
#ifndef CHRISBERGER_COMPLEX_H
#define CHRISBERGER_COMPLEX_H
#include <iostream>
using std::ostream;
using std::istream;
namespace ChrisBerger
{
class Complex
{
// prototype for friend << overloader
friend ostream &operator<<(ostream &out, const Complex &value);
// prototype for friend >> overloader
friend istream &operator>>(istream &in, Complex &value);
public:
// constructor with defualt values taking two argumnets
Complex(double real = 0, double imaginary = 0);
// prototype for + operator overloader
Complex operator+(const Complex &other) const;
// prototype for - operator overloader
Complex operator-(const Complex &other) const;
// prototype for == operator overloader
bool operator==(const Complex &other) const;
// prototype for != operator overloader
bool operator!=(const Complex &other) const;
private:
// data members for real and imaginary components
double real, imaginary;
};
}
#endif // !CHRISBERGER_COMPLEX_H
#include "Complex.h"
#include <iostream>
using std::ostream;
using std::istream;
// constructor taking two double arguments
ChrisBerger::Complex::Complex(double real, double imaginary) : real(real), imaginary(imaginary)
{ }
// + operator overloader
ChrisBerger::Complex ChrisBerger::Complex::operator+(const ChrisBerger::Complex &other) const
{
return Complex(real + other.real, imaginary + other.imaginary);
}
// - operator overloader
ChrisBerger::Complex ChrisBerger::Complex::operator-(const ChrisBerger::Complex &other) const
{
return Complex(real - other.real, imaginary - other.imaginary);
}
// == operator overloader
bool ChrisBerger::Complex::operator==(const ChrisBerger::Complex &other) const
{
return (other.real == real) && (other.imaginary == imaginary);
}
// != operator overloader
bool ChrisBerger::Complex::operator!=(const ChrisBerger::Complex &other) const
{
return !(other.real == real) && (other.imaginary == imaginary);
}
// << operator overloader
ostream &ChrisBerger::operator<<(ostream &out, const ChrisBerger::Complex &value)
{
// declare a temporary Complex
Complex *temp = new Complex(value.real, value.imaginary);
// output the real component
out << temp->real;
// if the imaginary component is negative...
if (temp->imaginary < 0)
{
// negate the imaginary component and output a minus-sign
temp->imaginary *= -1;
out << "-";
}
// if the imaginary component is positive, output a positive-sign
else
{
out << "+";
}
// output the "i"
out << temp->imaginary << "i";
// delete the temporary variable
delete temp;
return out;
}
// >> operator overloader
istream &ChrisBerger::operator>>(istream &in, ChrisBerger::Complex &value)
{
// declare a temporary charcter
char temp;
// declare doubles for the real and imaginary components
double tempReal, tempImaginary;
// read in the real component, this may include a minus-sign for a negative real component
in >> tempReal;
// set the real component of the Complex to the read in real component
value.real = tempReal;
// read in the sign of the imaginary component
in >> temp;
// read in the imaginary component which will be sans sign
in >> tempImaginary;
// if the sign of the imaginary component is negative, negate the imaginary component
if (temp == '-')
{
tempImaginary *= -1;
}
// set the imaginary component of the Complex to the read in complex component
value.imaginary = tempImaginary;
// read in the "i"
in >> temp;
return in;
}
Inheritance and Virtual Functions
#ifndef CHRISBERGER_SHAPES_H
#define CHRISBERGER_SHAPES_H
namespace ChrisBerger
{
// this abstract class serves as the base type for all other classes
class Shape
{
public:
// purely virtual function to display a Shape
virtual void display() const = 0;
};
// this abstract class is the parent class for Circle and Square types
class TwoDimensionalShape : public Shape
{
public:
// purely virtual function to get the area of a Shape
virtual double getArea() const = 0;
};
// this abstract class is the parent class for Sphere and Cube types
class ThreeDimensionalShape : public Shape
{
public:
// purely virtual function to get the surface area of a Shape
virtual double getSurfaceArea() const = 0;
// purely virtual function to get the volume of a Shape
virtual double getVolume() const = 0;
};
// class for a Shape of type Circle
class Circle : public TwoDimensionalShape
{
public:
// prototype for the Circle constructor
Circle(double radius);
// virtual function prototype to display a Circle
virtual void display() const;
// virtual function prototype to get the area of a Circle
virtual double getArea() const;
private:
// data member describing the radius of a Circle
double radius;
};
// class for a Shape of type Square
class Square : public TwoDimensionalShape
{
public:
// prototype for a Square constructor
Square(double lengthOfSide);
// virtual function prototype to display a Square
virtual void display() const;
// virtual function prototype to get the area of a Square
virtual double getArea() const;
private:
// data member describing the side length of a Square
double lengthOfSide;
};
// class for a Shape of type Sphere
class Sphere : public ThreeDimensionalShape
{
public:
// prototype for a Sphere constructor
Sphere(double radius);
// virtual function prototype to display a Sphere
virtual void display() const;
// virtual function prototype to get the surface area of a Sphere
virtual double getSurfaceArea() const;
// virtual function prototype to get the volume of a Sphere
virtual double getVolume() const;
private:
// data member describing the radius of a Sphere
double radius;
};
// class for a Shape of type Cube
class Cube : public ThreeDimensionalShape
{
public:
// prototype for a Cube constructor
Cube(double lengthOfSide);
// virtual function prototype to display a Cube
virtual void display() const;
// virtual function prototype to get the surface area of a Cube
virtual double getSurfaceArea() const;
// virtual function ptototype to get the volume of a Cube
virtual double getVolume() const;
private:
// data member describing the side length of a Cube
double lengthOfSide;
};
}
#endif // !CHRISBERGER_SHAPES_H
#include "Shapes.h"
#include <iostream>
using std::cout;
// constructor for a Circle taking one double argument
ChrisBerger::Circle::Circle(double radius) : radius(radius)
{ }
// virtual function to display a Circle
void
ChrisBerger::Circle::display() const
{
cout << "Circle with radius " << radius << " has area " << getArea() << "\n";
}
// virtual function to return the area of a Circle
double
ChrisBerger::Circle::getArea() const
{
return (3.14 * radius * radius);
}
// constructor for a Square taking one double argument
ChrisBerger::Square::Square(double lengthOfSide) : lengthOfSide(lengthOfSide)
{ }
// virtual function to display a Square
void
ChrisBerger::Square::display() const
{
cout << "Square with length of side " << lengthOfSide << " has area " << getArea() << "\n";
}
// virtual function to return the area of a Square
double
ChrisBerger::Square::getArea() const
{
return (lengthOfSide * lengthOfSide);
}
// constructor for a Sphere taking one double argument
ChrisBerger::Sphere::Sphere(double radius) : radius(radius)
{ }
// virtual function to display a Sphere
void
ChrisBerger::Sphere::display() const
{
cout << "Sphere with radius " << radius << " has surface area " << getSurfaceArea() <<
" and volume " << getVolume() << "\n";
}
// virtual function to return the surface area of a Sphere
double
ChrisBerger::Sphere::getSurfaceArea() const
{
return (4 * 3.14 * radius * radius);
}
// virtual function to return the volume of a Sphere
double
ChrisBerger::Sphere::getVolume() const
{
return (4 / 3 * 3.14 * radius * radius * radius);
}
// constructor for a Cube taking one double argument
ChrisBerger::Cube::Cube(double lengthOfSide): lengthOfSide(lengthOfSide)
{ }
// virtual function to display a Cube
void
ChrisBerger::Cube::display() const
{
cout << "Cube with length of side " << lengthOfSide << " has surface area " << getSurfaceArea() <<
" and volume " << getVolume() << "\n";
}
// virtual function to return the surface area of a Cube
double
ChrisBerger::Cube::getSurfaceArea() const
{
return (6 * lengthOfSide * lengthOfSide);
}
// virtual function to return the volume of a Cube
double
ChrisBerger::Cube::getVolume() const
{
return (lengthOfSide * lengthOfSide * lengthOfSide);
}
Templates
#ifndef ARRAY_H
#define ARRAY_H
#include <iostream>
#include <stdexcept>
using std::cout;
using std::invalid_argument;
namespace ChrisBerger
{
template <typename ElemType, int SIZE>
class Array
{
public:
//default constructor
Array()
{
cout << "Default Array constructor called\n";
}
//copy constructor
Array(const Array &source)
{
for (int i = 0; i < SIZE; i++)
{
// perform element-by-element copy (deep-copy)
elements[i] = source[i];
}
cout << "Copy constructor called\n";
}
// assignment operator overloader
Array operator=(const Array<ElemType, SIZE> &source)
{
for (int i = 0; i < SIZE; i++)
{
// perform element-by-element copy (deep-copy)
elements[i] = source[i];
}
}
// equality operator overloader
bool operator==(const Array<ElemType, SIZE> &source) const
{
for (int i = 0; i < SIZE; i++)
{
// if any element of the passed-in array does not match the object array,
// arrays are not equal, return false
if (elements[i] != source[i])
{
return false;
}
}
// all elements of the passed-in array match all elements of the object array, return true
return true;
}
// nonequality operator overloader
bool operator!=(const Array<ElemType, SIZE> &source) const
{
for (int i = 0; i < SIZE; i++)
{
// if any element of the passed-in array does not match any element of the object array,
// arrays are not equal, return true
if (elements[i] != source[i])
{
return true;
}
}
// all elements of the passed-in array match all elements of the object array, return false
return false;
}
// l-value subscript operator overloader
ElemType &operator[](int index)
{
// check to make sure the index is within range. if not, throw invalid_argument exception
if (index >= SIZE || index < 0)
{
throw invalid_argument("array index is out of range, thrown by L-value overloader");
}
return elements[index];
}
// r-value subscript operator overloader
ElemType operator[](int index) const
{
// check to make sure the index is within range. if not, throw invalid_argument exception
if (index >= SIZE || index < 0)
{
throw invalid_argument("array index is out of range, thrown by R-value overloader");
}
return elements[index];
}
private:
ElemType elements[SIZE];
};
}
#endif // !ARRAY_H
Function Overloading
// This program displays a character a user-specified number of times per row and for a user-specified
// number of rows.
#include <iostream> // standard I/O strearm "header" file
#include <cstdlib> // standard general "header" file
using namespace std; // make all names in "std" namespace visible
// number of types to prompt the user for input
const int NUM_PROMPTS = 5;
// function prototype for PrintLines with 3 input args
void PrintLines(int displayChar, int numColumns, int numRows);
// function prototype for PrintLines with 2 input args
void PrintLines(int displayChar, int numColumns);
// function prototype for PrintLines with 1 input arg
void PrintLines(int displayChar);
// function prototype for PrintLines with 0 input args
void PrintLines();
int main() // declare function main
{
for (int prompt = 0; prompt < NUM_PROMPTS; ++prompt) // loop numPrompts times
{
int numColumns, numRows; // declare num of characters and num of lines
char displayChar; // declare the character to print
//prompt the user to input the character to display, number of times to display, and number of rows
cout << "Enter character to display, number of types to display character on each "
"line, and number of lines to display: ";
// read in character to display, number of times to display, and number of rows
cin >> displayChar >> numColumns >> numRows;
PrintLines(int(displayChar), numColumns, numRows); // call PrintLines with 3 input args
PrintLines(int(displayChar), numColumns); // call PrintLines with 2 input args
PrintLines(int(displayChar)); // call PrintLines with 1 input args
PrintLines(); // call PrintLines with 0 input args
}
return(EXIT_SUCCESS); // return success to calling function
} // end function main
Recursion
// This file contains two functions. Inline function isSeparator returns a bool if its input is a separator
// Function Reverse Recursively reads one character at a time from a text file until a separator is
// encountered as determined by function isSeparator. Those non-separator characters are then displayed in
// reverse order, with the last character displayed being capitalized.
#include <fstream>
#include <iostream>
#include <cctype>
using namespace std;
inline bool isSeparator(int sepTest)
{
// if the input parameter sepTest is a separator as defined in the following list, return true
// otherwise, return false
return(isspace(sepTest) || (sepTest == '!') || (sepTest == ',') || (sepTest == ':') ||
(sepTest == ';') || (sepTest == EOF) || (sepTest == '.') || (sepTest == '?'));
}
int Reverse(ifstream &inFile, const int level)
{
// read in the next character from the file
int thisChar = inFile.get();
// if the character is a separator, return it to the caller
if (isSeparator(thisChar))
{
return(thisChar);
}
// if the character is not a separator...
else
{
// recursively recall this function, incrementing the recursion level
int thisSeparator = Reverse(inFile, level + 1);
// if the recursion level is one, it means we are ready to print the last character of the
// reversed word. It needs to be capitalized as well
if (level == 1)
{
cout << (char)toupper(thisChar);
}
// for any other recursion level, simply print the character
else
{
cout << (char)thisChar;
}
// return the result of the previous function call
return(thisSeparator);
}
}
The Standard Template Library (STL)
// This program demonstrates the usage of unit tests on deque, string, vector, and algorithm functionality
#include <iostream>
using std::cout;
#include <exception>
using std::clog;
#include <string>
using std::string;
#include <deque>
using std::deque;
using std::back_inserter;
#include <algorithm>
using std::generate_n;
using std::transform;
using std::partition;
using std::sort;
#include <numeric>
using std::accumulate;
#include <vector>
using std::vector;
#include <iterator>
using std::ostream_iterator;
#include <sstream>
using std::ostringstream;
// This function is used in dequeTest() to provide incremental numbers
int nextInt()
{
static int number = 1;
return number++;
}
// The unit test for demonstrating a deque
void dequeTest()
{
const int dequeLength = 10;
// 1+2+...+dequeLength
const int dequeAcc = 55;
// construct a deque
deque<int> ints(dequeLength);
try
{
// populate the deque with values 1 through dequeLength
generate_n(back_inserter(ints), dequeLength, nextInt);
clog << "dequeTest generate_n() PASSED\n";
}
catch (...)
{
clog << "dequeTest FAILED; generate_n() threw an exception\n";
}
// use the accumulate() algorithm to compute the sum of the values in the deque
if (accumulate(ints.begin(), ints.end(), 0) == dequeAcc)
{
clog << "dequeTest PASSED\n";
}
else
{
clog << "dequeTest FAILED: accumulate() did not return the correct value\n";
}
}
// this function returns the upper case of a char, used in stringTest()
char letterUpper(const char letter)
{
return toupper(letter);
}
// this function provides the unit test demonstrating a string
void stringTest()
{
// initialize a string to all lower case letters
string letters = "abcdefghijklmnopqrstuvwxyz";
try
{
// use the transform() algorithm to convert all lower case letters in the string to upper case
transform(letters.begin(), letters.end(), letters.begin(), letterUpper);
clog << "stringTest transform() PASSED\n";
}
catch (...)
{
clog << "stringTest FAILED: transform() threw an exception\n";
}
// check to make sure the transform() algorithm correctly performed the uppercase conversion
if (letters == "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
{
clog << "stringTest PASSED\n";
}
else
{
clog << "stringTest FAILED: string was not converted to uppercase\n";
}
}
// this function returns if the given argument is even, used in vectorTest()
bool isEven(int i)
{
return (i % 2) == 0;
}
// this function is a unit test demonstrating a vector
void vectorTest()
{
// define an array literal containing many numbers
int arrayLiteral[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
// compute the number of elements in the array
int arrayCount = sizeof(arrayLiteral) / sizeof(*arrayLiteral);
// initialize a vector of ints containing data from the literal array
vector<int> ints(arrayLiteral, arrayLiteral + arrayCount);
// initialize an interator for the vector
vector<int>::iterator evens;
try
{
// partition the array into even and odd numbers and store the location of the parition
evens = partition(ints.begin(), ints.end(), isEven);
// sort the first (even) partition
sort(ints.begin(), evens);
// sort the second (odd) partition
sort(evens, ints.end());
clog << "vectorTest partition() and sort() PASSED\n";
}
catch (...)
{
clog << "vectorTest FAILED: exception thrown during partition() or sort()\n";
}
try
{
// declare an ostringstream buffer
ostringstream buff;
// declare an ostream_interator using the buffer
ostream_iterator<int> output(buff);
// copy the vector into the ostringstream buffer
copy(ints.begin(), ints.end(), output);
if (buff.str() == "24681013579")
{
clog << "vectorTest PASSED\n";
}
else
{
clog << "vectorTest FAILED: Contents were not copied to ostream buffer\n";
}
}
catch (...)
{
clog << "vectorTest FAILED: exception thrown during copy() operation\n";
}
}
int main()
{
// call all of the unit tests
dequeTest();
stringTest();
vectorTest();
}
Example: Linked List
// This file contains the following functions:
// SafeMalloc() - safely allocates memory; causes the program to exit if allocation fails
// CreateListNode() - creates a new node and initializes its members
// Insert() - inserts a node into a linked list
// CreateList() - creates a singly-linked list from strings it reads from a text file. Each list node
// represents a unique string and the number of times it occurs in the file
// PrintList() - prints each string and the number of times it occurs in the file to the console
// FreeList() - frees the string members of each node, then frees every node.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "C2A6E4_List-Driver.h"
// maximum string size we will consider
#define MAX_STRING_LENGTH 256
static void *SafeMalloc(size_t memSize)
{
void *vp;
// Attempt to allocate memory. If allocation fails, output error to stderr and exit
if ((vp = malloc(memSize)) == NULL)
{
fputs("Could not allocate memory\n", stderr);
exit(EXIT_FAILURE);
}
// Return pointer to allocated memory
return(vp);
}
static List *CreateListNode(const char *str)
{
// allocate memory for a new node
List *newNode = (List *)SafeMalloc(sizeof(List));
// determine the length of the string; add 1 to include the string terminator '\0'
size_t strLength = strlen((const char *)str) + 1;
// allocate enough memory to store the string into the node
char *strAlloc = (char *)SafeMalloc(strLength);
// copy the string into the newly allocated memory
memcpy(strAlloc, str, strLength);
// point the new node string to the newly allocated memory containing the string
newNode->str = strAlloc;
// set the node count to 1, as this is the first time the string has appeared
newNode->count = 1;
return newNode;
}
static void Insert(List **pt, List *newNode)
{
// set the new node's next member to point to the previous node
newNode->next = *pt;
*pt = newNode;
}
List *CreateList(FILE *fp)
{
// declare firstNode, which will always be the first node of the list, and currentNode
List *firstNode = NULL, *currentNode = NULL;
// declare a character array that will temporarily store each new string as it is read in from the file
char nextString[MAX_STRING_LENGTH];
// read in the next string in the file, stop if EOF is encountered
while (fscanf(fp, "%255s", nextString) != EOF)
{
// begin by noting that we have not yet found a matching string.
int stringFound = 0;
// for every node
for (; currentNode != NULL; currentNode = currentNode->next)
{
// check if the new string from the file matches the string stored in the node if so...
if (strcmp(currentNode->str, nextString) == 0)
{
// increment the node count, indicating that we have found a duplicate string
currentNode->count++;
// specify that we have found a match
stringFound = 1;
// reset the node search to again start at the first node
currentNode = firstNode;
// break out of the for loop, we have found a match, no need to keep searching
break;
}
}
// if no matching string was found
if (!stringFound)
{
// create a new node, and store the new string in the node
currentNode = CreateListNode(nextString);
// place the new node at the beginning of the linked list
Insert(&firstNode, currentNode);
}
}
// return the first node of the linked list
return firstNode;
}
List *PrintList(const List *head)
{
List *nextNode = head;
// for every node...
for (; nextNode != NULL; nextNode = nextNode->next)
//print the string and count of the node
printf("%-20s %3d ea\n", nextNode->str, nextNode->count);
return((List *)head);
}
void FreeList(List *head)
{
// for every node...
while (head != NULL)
{
// temporarily create a new node that is equal to the node at the start of the linked list
List *nodePointer = head;
// prepare to advance to the next node
head = head->next;
// free the string stored in the node
free(head->str);
// free the node
free(nodePointer);
}
}
Example: Queue
// This program demonstrates a Queue container
#include <iostream>
using std::cout;
using std::copy;
#include <stdexcept>
using std::logic_error;
#include <assert.h>
const int numElems = 10;
const int elems[numElems] = { 0,1,2,3,4,5,6,7,8,9 };
const int initialSize = 3;
template <typename T>
class Queue
{
public:
Queue(); // Construct empty queue
~Queue(); // Destructor
Queue(const Queue &); // Copy constructor
Queue &operator=(const Queue &); // Copy assignment operator
void push(const T &); // Add element to back of queue
void pop(); // Remove front element from queue
T &front(); // Return ref to front element in queue
const T &front() const; // Return const ref to front element in queue
bool empty() const; // Return whether queue is empty
size_t size() const; // vused_ accessor
private:
T* v_; // Elements in queue
size_t vsize_; // Max number of elements in queue
size_t vused_; // Number of elements in queue
};
template<typename T>
Queue<T>::Queue() : v_(static_cast<T *>(operator new(sizeof(T) * initialSize))), vsize_(initialSize), vused_(0)
{ }
template<typename T>
Queue<T>::~Queue()
{
destroy(v_, v_ + vused_);
operator delete(v_);
}
// Helper function for the copy constructor, performs the copy
template<typename T>
T* newCopy(const T *src, size_t srcsize, size_t destsize)
{
// Ensure enough memory will be allocated in the destination
assert(destsize >= srcsize);
// Allocate memory for the destination copy
T* dest = new T[destsize];
try
{
// Perform the copy
copy(src, src + srcsize, dest);
}
catch (...)
{
// If there was a problem, free destination memory
delete[] dest;
throw;
}
return dest;
}
// Copy constructor
template<typename T>
Queue<T>::Queue(const Queue<T> &other)
: v_(newCopy(other.v_, other.vsize_, other.vsize_)), vsize_(other.vsize_), vused_(other.vused_)
{ }
// Copy assignment operator
template<typename T>
Queue<T>& Queue<T>::operator=(const Queue<T> &other)
{
// if the two variables of the assignment aren't already equal, perform the assignment
if (this != &other)
{
// Create a new copy of the RHS variable
T* v_new = newCopy(other.v_, other.vsize_, other.vsize_);
// Clear the LHS variable buffer
delete[] v_;
// Make the LHS variable data members equal to the RHS variable data members
v_ = v_new;
vsize_ = other.vsize_;
vused_ = other.vused_;
}
return *this;
}
// Push a new value to the back of the queue
template<typename T>
void Queue<T>::push(const T &t)
{
// If the queue is out of space, allocate new space for more queue items
if (vused_ == vsize_)
{
// double the size of the new queue
size_t vsize_new = vsize_ * 2 + 1;
cout << "Increasing size of Queue. Current size: " << vused_ << ", new size: " << vsize_new << "\n";
// allocate new memory for a larger queue, but do not construct any objects
T *v_new_safe = static_cast<T *>(operator new(vsize_new * sizeof(T)));
// construct each object for the new queue
for (size_t i = 0; i < vused_; ++i)
{
construct(v_new_safe + i, v_[i]);
}
// copy elements from the old small queue into the new large queue
swap(v_, v_new_safe);
// destroy the objects in the temporary queue
destroy(v_new_safe);
// deallocate memory from the temporary queue
operator delete(v_new_safe);
vsize_ = vsize_new;
}
// push the new value to the end of the queue
construct(v_ + vused_, t);
// increment number of items in the queue
++vused_;
}
// pop the front value from the queue
template<typename T>
void Queue<T>::pop()
{
// if the queue is empty, cannot pop a value; throw
if (vused_ == 0)
{
throw logic_error("Error: attempt to pop from empty queue");
}
// otherwise, move all of the elements of the queue one closer to the front
// and decrement vused_
// I am guessing this is absurdly inefficient but it works
else
{
for (size_t element = 1; element <= vused_; ++element)
{
v_[element - 1] = v_[element];
}
--vused_;
}
}
// Constructs a new object in a given location using a given value
template <typename T1, typename T2>
void construct(T1 *p, const T2 &value)
{
new (p) T1(value);
}
// Destroy an object (but does not deallocate any memory)
template <typename T>
void destroy(T *p)
{
p->~T();
}
// Destroy an object range (but does not deallocate any memory)
template <typename FwdIter>
void destroy(FwdIter first, FwdIter last)
{
while (first != last)
{
destroy(&*first);
++first;
}
}
// Exchange the values of two objects
template <typename T>
void swap(T &a, T &b)
{
T temp(a);
a = b;
b = temp;
}
// Return first element in queue
template<typename T>
T & Queue<T>::front()
{
// if the queue is empty, throw an exception and report to user
if (vused_ == 0)
{
throw logic_error("Error: empty queue");
}
// otherwise, return the first element in the queue
else
{
return v_[0];
}
}
// Return first element in queue
template<typename T>
const T & Queue<T>::front() const
{
// if the queue is empty, throw an exception and report to user
if (vused_ == 0)
{
throw logic_error("Error: empty queue");
}
// otherwise, return the first element in the queue
else
{
return v_;
}
}
// Determine whether queue is empty
template<typename T>
bool Queue<T>::empty() const
{
return (vused_ == 0);
}
// vused_ accessor
template<typename T>
size_t Queue<T>::size() const
{
return vused_;
}
int main()
{
Queue<int> myQueue;
cout << "Is the queue empty? Answer: " << myQueue.empty() << "\n";
for (int i = 0; i < numElems; ++i)
{
cout << "Pushing " << elems[i] << " to queue\n";
myQueue.push(elems[i]);
}
cout << "Is the queue empty? Answer: " << myQueue.empty() << "\n";
cout << "The current front element is " << myQueue.front() << " and the current size is " << myQueue.size() << "\n";
for (int i = 0; i < numElems-1; ++i)
{
cout << "Popping " << myQueue.front() << ". ";
myQueue.pop();
cout << "The current front element is " << myQueue.front() << " and the current size is " << myQueue.size() << "\n";
}
Queue<int> myQueue2;
for (int i = 0; i < numElems; ++i)
{
cout << "Pushing " << elems[i] << " to queue 2\n";
myQueue2.push(elems[i]);
}
Queue<int> myQueue3;
myQueue3 = myQueue2;
cout << "Queue 3 front: " << myQueue3.front() << ", queue 2 front: " << myQueue2.front() << "\n";
cout << "Queue 3 size: " << myQueue3.size() << ", queue 2 size: " << myQueue2.size() << "\n";
}