Question

I'm currently going through Accelerated C++ and I'm having a little bit of difficulty in understanding where I necessarily need to put #include's (whether in the header and/or source file) and mostly, whether I'm being redundant.

I'd appreciate all comments on what I should change and what's not necessary. I feel like I'm including libraries multiple times (e.g. if the main program has #include, do I need to have it in any of the other header/source files?). Also, if use

#include "Student_info.h" 

and the Student_info.cpp file includes grade.h, do I still need to include grade.h in main.cpp or is this sufficient? I posted this code because I think application to a specific example would be the best way to drill in my head the proper way to organize these files and also common mistakes that I should watch out for

LIST OF FILES - I'm including the entire code for completeness but only the top parts and principles really matter

main.cpp

#include<iostream>
#include<string>
#include<ios>
#include<stdexcept>
#include<vector>
#include<algorithm>
#include<iomanip>
#include "grade.h"
#include "Student_info.h"
#include "analysis.h"



using std::cin;           using std::setprecision;
using std::cout;          using std::sort;
using std::domain_error;  using std::streamsize;
using std::endl;          using std::string;
using std::max;           using std::vector;

int main()
{
    //students who did and didn't do their homework
    vector<Student_info> did, didnt;

    //read the student records and partition them
    Student_info student;
    while (read(cin,student))
    {
        if (did_all_hw(student))
            did.push_back(student);
        else
            didnt.push_back(student);

    }

    //verify that the analyses will show us something
    if (did.empty())
    {
        cout<<"No student did all the homework!"<<endl;
        return 1;

    }
    if (didnt.empty())
    {
        cout<<"Every student did all the homework!"<<endl;
        return 1;
    }

    //do the analyses
    write_analysis(cout, "median",median_analysis, did, didnt);
    write_analysis(cout, "average",average_analysis, did, didnt);
    write_analysis(cout, "median of homework turned in", optimistic_median_analysis, did, didnt);

    return 0;
}

grade.h

#ifndef GUARD_grade_h
#define GUARD_grade_h

//grade.h - Header file
#include<vector>
#include "Student_info.h"


double grade(double, double, double);
double grade(double, double, const std::vector<double>&);
double grade(const Student_info&);

bool fgrade(const Student_info& s);
bool pgrade(const Student_info& s);

double grade_aux(const Student_info& s);

double average(const std::vector<double>& v);
double average_grade(const Student_info& s);
double optimistic_median(const Student_info& s);

#endif

grade.cpp

#include<stdexcept>
#include<vector>
#include<numeric>
#include "grade.h"
#include "median.h"
#include "Student_info.h"

using std::domain_error;
using std::vector;


//reference to constant vector of type double
//vector<double> homework
//vector<double>& hw = homework; (this means that hw is a synonym for homework)
//This function is a ALSO called grade
//This is OVERLOADING and the argument list will be checked to decide which function to call
//The & asks the implementation NOT to copy its argument and const means don't change it
double grade(double midterm, double final, const vector<double>& hw)
{
    if (hw.size()==0)
        throw domain_error("student has done no homework");
    return grade(midterm,final,median(hw));

}

double grade(double midterm, double final, double homework)
{
    return 0.2*midterm+0.4*final+0.4*homework;
}


//because it's a reference variable, there is no overboard in copying the object
//and modifying it in the function modifies the original variable passed in
//returns double representing OVERALL GRADE
double grade(const Student_info& s)
{
    return grade(s.midterm,s.final,s.homework);
}

bool fgrade(const Student_info& s)
{
    return grade(s) < 60;
}

bool pgrade(const Student_info& s)
{
    return !fgrade(s);
}

//need this function because the transform function can't use grade as there are too many overloaded versions
//and it doesn't know which one to call
double grade_aux(const Student_info& s)
{
    try {
        return grade(s);
    }
    catch (domain_error)
    {
        return grade(s.midterm, s.final, 0);
    }
}

//Write the average function to use for average_analysis
double average(const vector<double>& v)
{
    //accumulate is defined in the <numeric> library
    //The accumulate function adds the values in range denoted by the first two arguments, starting with the
    //the value given by the third argument (which also gives the resulting type, e.g. 0.0 will make accumulate return a double
    return accumulate(v.begin(),v.end(),0.0)/v.size();
}

double average_grade(const Student_info& s)
{
    return grade(s.midterm,s.final, average(s.homework));
}


double optimistic_median(const Student_info& s)
{
    vector<double> nonzero;
    //extracts nonzero vectors from the homework vector and appends them to  (vector<double> nonzero)
    remove_copy(s.homework.begin(), s.homework.end(),
                back_inserter(nonzero),0);

    if (nonzero.empty())
        return grade(s.midterm,s.final,0);
    else
        return grade(s.midterm, s.final,median(nonzero));
}

analysis.h

#ifndef GUARD_output_analysis
#define GUARD_output_analysis
//output_analysis.h - header file
#include<iostream>
#include<vector>
#include<iterator>
#include "Student_info.h"
#include "grade.h"
#include "median.h"


using std::ostream; using std::string;



void write_analysis(ostream& out,
                    const string& name,
                    double analysis(const vector<Student_info>&),
                    const vector<Student_info>& did,
                    const vector<Student_info>& didnt);


double median_analysis(const vector<Student_info>& students);
double average_analysis(const vector<Student_info>& students);
double optimistic_median_analysis(const vector<Student_info>& students);



#endif

analysis.cpp

#include<iterator>
#include<algorithm>
#include "Student_info.h"
#include "grade.h"
#include "median.h"

using std::istream; using std::vector;
using std::ostream; using std::string;
using std::endl;    using std::transform;


void write_analysis(ostream& out,
                    const string& name,
                    double analysis(const vector<Student_info>&),
                    const vector<Student_info>& did,
                    const vector<Student_info>& didnt)
{
    out <<name <<" : median(did) = "<<analysis(did)
        <<", median(didnt) = "<<analysis(didnt)<<endl;

}

double median_analysis(const vector<Student_info>& students)
{
    vector<double> grades;

    //The function grade is applied to every element in the range students.begin() to students.end()
    //and this is appended to the end of the grades vector
    transform(students.begin(), students.end(), back_inserter(grades), grade_aux);

    return median(grades);
}

double average_analysis(const vector<Student_info>& students)
{
    vector<double> grades;

    //recall that tranform applies the average_grade function to every element between students.begin() and students.end()
    //and appends this to the end of the grade vector (which is where the iterator returned by back_inserter(grades) points to
    transform(students.begin(), students.end(),
              back_inserter(grades), average_grade);

    return median(grades);
}

double optimistic_median_analysis(const vector<Student_info>& students)
{
    vector<double> grades;

    transform(students.begin(),students.end(), back_inserter(grades),optimistic_median);

    return median(grades);

}

median.h

#ifndef GUARD_median_h
#define GUARD_median_h

//median.h
#include<vector>
double median(std::vector<double>);

#endif

median.cpp

#include "median.h"

using std::vector;
using std::sort;
using std::domain_error;

double median(vector<double> vec)
{
    typedef vector<double>::size_type vec_sz;

    vec_sz size=vec.size();

    //if the vector is empty, an exception is thrown
    //execution stops and passes to another part of the program
    //along with the exception object
    if (size==0)
        throw domain_error("median of an empty vector");

    sort(vec.begin(),vec.end());

    vec_sz mid = size/2;
    return size%2==0 ? ((vec[mid]+vec[mid-1])/2) : (vec[mid]);

}

Student_info.h

#ifndef GUARD_Student_info
#define GUARD_Student_info

//Student_info.h header file

#include<iostream>
#include<string>
#include<vector>

using std::vector;


struct Student_info {
    std::string name;
    double midterm,final;
    std::vector<double> homework;
};

bool compare(const Student_info&, const Student_info&);
bool did_all_hw(const Student_info& s);

bool pgrade(const Student_info& s);
std::istream& read(std::istream&, Student_info&);
std::istream& read_hw(std::istream&, std::vector<double>&);

vector<Student_info> extract_fails(vector<Student_info>& students);


#endif

Student_info.cpp

//Source file for Student_info related functions
#include "Student_info.h"
#include "grade.h"

using std::istream; using std::vector;

bool compare(const Student_info& x, const Student_info& y)
{
    return x.name < y.name;
}

istream& read(istream& is, Student_info& s)
{
    //read and store the student's name and midterm and final exam grades
    is >> s.name >> s.midterm >> s.final;

    read_hw(is, s.homework); //read AND store all the student's homework grades
    return is;

}

istream& read_hw(istream& in, vector<double>& hw)
{
    if (in) {

        //remove previous contents and leaves us with an empty vector
        hw.clear();

        double x;
        while (in>>x)
            hw.push_back(x);

        //clear the stream so that input will work for the next student
        //reset any error indicatiosn so that input can continue
        in.clear();
    }

    //this means we were given an object that we're NOT going to a copy and we will
    //return the object without copying it

    return in;
}


bool did_all_hw(const Student_info& s)
{
    //recall  that if 0 is not found in the sequence given by the first two iterators, then the 2nd argument is returned
    return ((find(s.homework.begin(), s.homework.end(), 0)) == s.homework.end());

}

//ONE-PASS SOLUTION
vector<Student_info> extract_fails(vector<Student_info>& students)
{
    vector<Student_info>::iterator iter=stable_partition(students.begin(),students.end(),pgrade);
    vector<Student_info> fail(iter,students.end()); //vector consisting of all failing elements

    students.erase(iter,students.end());
    return fail;
}
Was it helpful?

Solution

You only need to include a header file if the compiler needs to know the size of the type you're using or if you attempt to access any of it's members or methods (including methods which you haven't explicitly created, such as the default constructor, destructor or assignment operators). In Grade.h you don't need to #include "Student_info.h" as Student_info is a reference. You can instead use a forward declaration:

class Student_info; // Don't #include it, forward declare it

Also, you would rarely need to write a header guard, as all modern compilers support #pragma once which only includes that file once during compilation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top