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(&registrants[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";
}