0

I'm having a problem when I'm printing these arrays of students for an assignment. I think the problem lies in the copy constructor in Course.cpp. In the main class "TestCourse.cpp" I fill course objects with class and student information w/ course objects course1, course2, and course3. course3 copies the attributes of course1 using the copy constructor and that's displayed in the output.

The Issue: If you look at the course1 output it adds the first new student("New Student 1") I add to course3 after I copy it, but only in the first course1 output. It does this if I copy course2 as well. How can I fix this/ what am I doing wrong? assignments already turned in but it never hurts to know.code below:

Header File

 /*
 * Course.h
 *
  *HEADER FILE
 */

#ifndef COURSE_H_
#define COURSE_H_
#include <string>

namespace std {

class Course {
public:
Course(const string& CourseName, int Capacity);
Course(const Course&);
 ~Course();
 //declaration for copy constructor goes here
 string getCourseName() const;
 void addStudent(const string& Name);
 void dropStudent(const string& Name);
 string* getStudents() const;
 int getNumberOfStudents() const;
 int getCapacity();
 void Clear();
 //declaration for clear function goes here

 private:
 string CourseName;
 string* Students;
 int NumberOfStudents;
 int Capacity;
};

} /* namespace std */

#endif /* COURSE_H_ */`

** Course.cpp containing the functions**

    /*
     * Course.cpp
     *
     *  Created on: Nov 4, 2016
     *      Author: diez
     */

    #include "Course.h"
    #include <iostream>
    #include <string>
    #include <iterator>

    namespace std {

    //Course Constuctor
    Course::Course(const string& CourseName, int Capacity) {

    NumberOfStudents = 0;
    this-> CourseName = CourseName;
    this-> Capacity = Capacity;
    Students = new string[Capacity];
       }//end constructor

    //Destructor
    Course::~Course() 
    {
     delete [] Students;
    }//END Destructor

    //Getter for Course Name
    string Course::getCourseName() const
    {
    return CourseName;
       }//END get

    // Code to add Student to Course Object
    void Course::addStudent(const string& Name)
    {



    //new array w/ doubly capacity
                string* newArr;
                newArr= new string[Capacity * 2];
        if(NumberOfStudents >= Capacity)
        {

            Capacity *= 2;

        //Loop setting new array equal to old array
        for ( int i = 0; i < getNumberOfStudents(); i++)
        {
            newArr[i] = Students[i];
        }
        //Sets old array to new array's capacity and information
        Students = newArr;
    }//endif

        //adds students to array
        Students[NumberOfStudents] = Name;
        NumberOfStudents++;
    }//END ADD STUDENT

/*******************************
 * DROP STUDENT FUNCTION
 *****************************/
void Course::dropStudent(const string& Name){

    /* For Loops looks through array elements*/
    for(int i=0; i< getNumberOfStudents(); i++)
        {
        /* If element is the specified student */
            if(Students[i]==Name)
            {
                /*Set element equal to the next */
                for(int j=i; j<(getNumberOfStudents()); j++)
                {
                    Students[j]=Students[j+1];

                }
            }
        }
    NumberOfStudents--;

}// DROP STUDENT

string* Course::getStudents() const{
    return Students;
}
int Course::getNumberOfStudents() const{
    return NumberOfStudents;
}
int Course::getCapacity() {
    return Capacity;
}

/********************************
 * CLEAR FUNCTION
 ************************************/
void Course::Clear(){
    for (int i = 0; i < NumberOfStudents; i++){
        Students[i] = " ";
    }
    NumberOfStudents = 0;

}//CLEAR

/******************
*COPY CONSTRUCTOR
*****************/
Course::Course(const Course& Course){
    CourseName = Course.CourseName;
    Capacity = Course.Capacity;
    NumberOfStudents = Course.NumberOfStudents;
    Students = Course.Students;
    }
    } /* namespace std */

** Driver Class **

//============================================================================
// Name        :
// Author      : 
// Date        : 11/04/16
// Copyright   : Your copyright notice
// Description :
/*             :Program does not include double capacity function rather, doubles the capacity in the addStudent function
 :Program implements the dropStudent function
 :Program includes a new function named clear() that removes all of the students from the course
 :Program implements the destructor and copy constructor to perform a deep copy in the class
 :A third course is created that adds five students, removes one and displays the students in the course.*/
//             :
//============================================================================
#include <iostream>
#include "Course.h"
using namespace std;

int main() {

    //instances of Course w/ name and capacity
    Course course1("Data Structures", 10);
    Course course2("Database Systems", 15);

    course1.addStudent("Peter Jones");
    course1.addStudent("Lisa Walker");
    course1.addStudent("Kevin Medara");

    course1.addStudent("Joe Sansone");
    course1.addStudent("Shari Bemis");
    course1.addStudent("Vishnu");
    course1.addStudent("Kieth Naeck");


    //course 2 adding
    course2.addStudent("Jacob Fraser");
    course2.addStudent("Bob Walker");
    course2.addStudent("Kevin Medara");
    course2.addStudent("Joe Sansone");
    course2.addStudent("Shari Bemis");
    course2.addStudent("Melissa Johnson");

    // USES COPY CONSTRUCTOR in 'Course.cpp'
    Course course3(course1);
    /*
     * ADD 5 STUDENTS TO COURSE 3
     * and force array expansion
     */

    course3.addStudent("NEW STUDENT 1");
    course3.addStudent("NEW STUDENT 2");
    course3.addStudent("NEW STUDENT 3");
    course3.addStudent("NEW STUDENT 4");
    course3.addStudent("NEW STUDENT 5");

    /**************************************************
     * COURSE 1 CONSOLE OUTPUT
     ***************************************************/

    /*               **NOTE**
     * each course output has the same properties, course3 copies the properties
     * of course1
     */
    cout << "Class: " << course1.getCourseName() << " | Number Of Students: "
            << course1.getNumberOfStudents() << "\nStudents In Course One "
            << " Capacity: " << course1.getCapacity() << endl; // prints !!!Hello World!!!

    /*pointer string array to iterate through course Students*/
    string* students = course1.getStudents();

    for (int i = 0; i <= course1.getNumberOfStudents(); i++) {

        cout << i << " " << students[i] << endl;

    }

    cout << "_____________________________________" << endl;

    /*************************************************
     * COURSE 2 CONSOLE OUTPUT
     *************************************************/
    cout << "Class: " << course2.getCourseName() << " | Number Of Students: "
            << course2.getNumberOfStudents() << "\nStudents In Course Two "
            << " Capacity: " << course2.getCapacity() << endl;
    students = course2.getStudents();
    for (int i = 0; i <= course2.getNumberOfStudents(); i++) {
        cout << i << " " << students[i] << "\n";
    }
    cout << "______________________________________" << endl;

    /*********************
     * COURSE 3 CONSOLE OUTPUT
     *deep copies console 1
     *************************************/

    cout << "Class: " << course3.getCourseName() << " | Number Of Students: "
            << course3.getNumberOfStudents() << "\nStudents In Course Three "
            << " Capacity: " << course3.getCapacity() << endl;
    students = course3.getStudents();

    for (int i = 0; i <= course3.getNumberOfStudents(); i++) {
        cout << i << " " << students[i] << "\n";
    }

    cout << "_____________________________________" << endl;

    /***********************************************************************************
     * CALL DROP STUDENT FUNCTION on course1
     * w/ print out
     *******************************************/
    course1.dropStudent("Kevin Medara");

    cout << "Course One after dropping myself\n" << "Class: "
            << course1.getCourseName() << " | Number Of Students: "
            << course1.getNumberOfStudents() << "\nStudents In Course One "
            << " Capacity: " << course1.getCapacity() << endl;
    students = course1.getStudents();

    for (int i = 0; i < course1.getNumberOfStudents(); i++) {
        cout << i << " " << students[i] << endl;
    }

    cout << "_____________________________________" << endl;

    /**********************
     * CALL CLEAR FUNCTION OF COURSE 1
     * w/ print out
     ****************************/
    course1.Clear();

    cout << "Cleared Course One\n" << "Class: " << course1.getCourseName()
            << " | Number Of Students: " << course1.getNumberOfStudents()
            << "\nStudents In Course One " << " Capacity: "
            << course1.getCapacity() << endl;

    students = course1.getStudents();

    for (int i = 0; i <= course1.getNumberOfStudents(); i++) {

        cout << i <<" " << students[i] << endl;

    }

    cout << "_____________________________________" << endl;

    /**********************************
     * DROP STUDENT FROM COURSE 3
     * w/ print
     ************************************/

    course3.dropStudent("Shari Bemis");

    cout << "Course 3 After Dropping Shari Bemis\n" << "Class: "
            << course3.getCourseName() << " | Number Of Students: "
            << course3.getNumberOfStudents() << "\nStudents In Course Three "
            << " Capacity: " << course3.getCapacity() << endl;
    students = course3.getStudents();

    for (int i = 0; i <= course3.getNumberOfStudents(); i++) {

        cout << i <<" "<< students[i] << "\n";

    }

    cout << "_____________________________________" << endl;

    return 0;
}

EDIT:: Here is the output

Class: Data Structures | Number Of Students: 7
Students In Course One  Capacity: 10
0 Peter Jones
1 Lisa Walker
2 Kevin Medara
3 Joe Sansone
4 Shari Bemis
5 Vishnu
6 Kieth Naeck
7 NEW STUDENT 1
_____________________________________
Class: Database Systems | Number Of Students: 6
Students In Course Two  Capacity: 15
0 Jacob Fraser
1 Bob Walker
2 Kevin Medara
3 Joe Sansone
4 Shari Bemis
5 Melissa Johnson
6 
______________________________________
Class: Data Structures | Number Of Students: 12
Students In Course Three  Capacity: 20
0 Peter Jones
1 Lisa Walker
2 Kevin Medara
3 Joe Sansone
4 Shari Bemis
5 Vishnu
6 Kieth Naeck
7 NEW STUDENT 1
8 NEW STUDENT 2
9 NEW STUDENT 3
10 NEW STUDENT 4
11 NEW STUDENT 5
12 
_____________________________________
Course One after dropping myself
Class: Data Structures | Number Of Students: 6
Students In Course One  Capacity: 10
0 Peter Jones
1 Lisa Walker
2 Joe Sansone
3 Shari Bemis
4 Vishnu
5 Kieth Naeck
_____________________________________
Cleared Course One
Class: Data Structures | Number Of Students: 0
Students In Course One  Capacity: 10
0  
_____________________________________
Course 3 After Dropping Shari Bemis
Class: Data Structures | Number Of Students: 11
Students In Course Three  Capacity: 20
0 Peter Jones
1 Lisa Walker
2 Kevin Medara
3 Joe Sansone
4 Vishnu
5 Kieth Naeck
6 NEW STUDENT 1
7 NEW STUDENT 2
8 NEW STUDENT 3
9 NEW STUDENT 4
10 NEW STUDENT 5
11 
_____________________________________
  • 4
    are you forced to use manual dynamic allocation? If not you should use a `std::vector` instead of `string*` – 463035818_is_not_an_ai Nov 09 '16 at 20:49
  • `namespace std` can interfere with the one defining string and iostream... – Raindrop7 Nov 09 '16 at 20:52
  • Yes, sadly we are =/ –  Nov 09 '16 at 20:53
  • `Students = Course.Students;` is a shallow copy; it will copy the pointer to the array, but all Courses will share the same data in that array. (Assuming the memory is still valid! If your first Course goes out of scope you will be left with a dangling pointer.) You probably want a deep copy instead. – 0x5453 Nov 09 '16 at 20:53
  • my book does say to do this with something like Students = new string[Capacity]. but that was giving me a blank array 0_o –  Nov 09 '16 at 21:02
  • `Students = new string[Capacity]` will provide storage, but it won't copy the contents of the pre-existing array into the new array. You can use a `for` loop or [`std::copy` for that.](http://en.cppreference.com/w/cpp/algorithm/copy) It looks like your instructor, book, or whatever is trying to teach [The Rule of Three](http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). Know it. Live by it. It will save you tonnes of debugging. – user4581301 Nov 09 '16 at 22:13
  • @user4581301 So judging from that rule of three link I should add a copy assignment operator as well? –  Nov 10 '16 at 02:41
  • Yes. A missing copy constructor is typically more dangerous due to the threat of an accidental pass by value, but without `operator=` making sure the deep copy takes place, you are one assignment away from ruining your whole day. – user4581301 Nov 10 '16 at 04:12

1 Answers1

1

Changed the copy constructor to:

Course::Course(const Course& Course)
{
    CourseName = Course.CourseName;
    Capacity = Course.Capacity;
    NumberOfStudents = Course.NumberOfStudents;
    cout << &Students << endl;
    cout<< Course.Students;

    //allocate the new memory
    Students = new string[Capacity];

    //actually copy over the data, which was apparently the problem
    for(int i = 0; i < NumberOfStudents; i ++)
    {
        Students[i] = Course.Students[i];
    }

}
  • the two cout lines are just for checking the two are pointing to separate locations –  Nov 10 '16 at 03:52
  • Another useful and under taught (in my opinion) C++-ism is the [Member Initializer List](http://en.cppreference.com/w/cpp/language/initializer_list). An absolute must when you have objects inheriting a base class without a default constructor or your object contains a member variable with expensive construction logic that will be reassigned inside the constructor's body. Doesn't help much here, but you could replace constructing empty `strings` and then assigning with a call of the `string` copy constructor. – user4581301 Nov 10 '16 at 04:18