michaellindahl
michaellindahl

Reputation: 2052

Preventing Object Slicing - C++

I am trying to create a vector of Person objects. However I really want to store Adult, Professor, and Student objects which are derived from the Person base class. After asking this question I have learned that my issue was in Object Splicing and that it would save the derived classes as a Person class because that is what the vector is of. I have attempted to modify the code to rectify the situation however I still am coming across some errors and need some additional assistance. Thanks!

Here is my main.cpp code:

#include "Person.h"
#include "Professor.h"
#include "Student.h"
#include "Adult.h"
#include <vector>
#include <string>
#include <iostream>
using namespace std;

template<typename T> void addPerson(vector<Person *> &personVector) {
    cout << "DEBUG" << endl;
    personVector.push_back(new T());
}

void addFriend(vector<Person *> &personVector) {
    bool loop = true;
    while (loop) {
        cout << "\nWhich Person would you like to create? " << endl;
        cout << "  1. Professor" << endl;
        cout << "  2. Student" << endl;
        cout << "  3. Adult" << endl;
        int caseVar;
        cin >> caseVar;
        switch (caseVar) {
        case 1: 
            addPerson<Professor>(personVector);
            // old incorrect line: addPerson<Professor *>(personVector);
            loop = false;
            break;
        case 2: 
            addPerson<Student>(personVector);
            // old incorrect line: addPerson<Student *>(personVector);
            loop = false;
            break;
        case 3: 
            addPerson<Adult>(personVector); 
            // old incorrect line: addPerson<Adult *>(personVector);
            loop = false;
            break;
        default: cout << "Unknown Entry Please Try Again." << endl;
        }
    }
}

void displayFriends(vector<Person *> &personVector) {
    if (personVector.size() > 1) {
        for (unsigned i = 0; personVector.size() > i; i++)
            cout << i+1 << ". " << personVector[i]->getName() << endl;
            cout << "DEBUG" << endl;
    }
}

void viewFriend(vector<Person *> &personVector) {
    if (personVector.size() > 1) {
        displayFriends(personVector);
        cout << "Which # friend would you like to view? ";
        int num;
        cin >> num;
        personVector[num-1]->coutPerson();
    } else if (personVector.size() == 1) {
        personVector[0]->coutPerson();
    } else {
        cout << "No friends to View." << endl;
    }
}


void deleteElementFromArray(int element, vector<Person *> &personVector) {
    vector<Person *> newPersonVector;
    for (unsigned i = 0; personVector.size() > i; i++)
        if (i != element - 1)
            newPersonVector.push_back(personVector[i]);
    personVector = newPersonVector;
}

void removeFriend(vector<Person *> &personVector) {
    if (personVector.size() > 1) {
        displayFriends(personVector);
        cout << "Which # friend would you like to remove? ";
        unsigned num;
        cin >> num;
        if (num <= personVector.size())
            deleteElementFromArray(num, personVector);
        else
            cout << "Invalid Selection" << endl;
    } else if (personVector.size() == 1) {
        cout << "Removed one and only friend" << endl;
    } else {
        cout << "No friends to Remove." << endl;
    }
}


int main() {
    vector<Person *> personVector;
    // Run Main Menu
    cout << "Friends with Stuff" << endl;
    cout << "Adding 5 friends to the list" << endl;
    personVector.push_back(new Person("James"));
    personVector.push_back(new Person("Martin"));
    personVector.push_back(new Person("Sammi"));
    personVector.push_back(new Person("Donny"));
    personVector.push_back(new Person("Ronald"));
    bool loop = true;
    while (loop) {
        cout << "\nWhat would you like to do? " << endl;
        cout << "  1. Add New Friend" << endl;
        cout << "  2. View Friend" << endl;
        cout << "  3. Remove Friend" << endl;
        cout << "  4. Clear Friends" << endl;
        cout << "  5. Get Object" << endl;
        cout << "  6. Exit" << endl;
        int caseVar;
        cin >> caseVar;
        switch (caseVar) {
        case 1: 
            addFriend(personVector);
            break;
        case 2: 
            viewFriend(personVector);
            break;
        case 3: 
            removeFriend(personVector);
            break;
        case 4: 
            personVector.clear();
            break;
        case 5:

            break;
        case 6: 
            loop = false; 
            break;
        default: cout << "Unknown Entry Please Try Again." << endl;
        }
    }
}

I am getting the following errors

InitializeBuildStatus:
1>  Touching "Debug\FPVisualStudio.unsuccessfulbuild".
1>ClCompile:
1>  main.cpp
1>\\cs1\cs_students\mlindahl15\cs172\final project\fpvisualstudio\main.cpp(12): error C2664: 'void std::vector<_Ty>::push_back(_Ty &&)' : cannot convert parameter 1 from 'Professor **' to 'Person *&&'
1>          with
1>          [
1>              _Ty=Person *
1>          ]
1>          Reason: cannot convert from 'Professor **' to 'Person *'
1>          Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>          \\cs1\cs_students\mlindahl15\cs172\final project\fpvisualstudio\main.cpp(26) : see reference to function template instantiation 'void addPerson<Professor*>(std::vector<_Ty>)' being compiled
1>          with
1>          [
1>              _Ty=Person *
1>          ]
1>\\cs1\cs_students\mlindahl15\cs172\final project\fpvisualstudio\main.cpp(12): error C2664: 'void std::vector<_Ty>::push_back(_Ty &&)' : cannot convert parameter 1 from 'Student **' to 'Person *&&'
1>          with
1>          [
1>              _Ty=Person *
1>          ]
1>          Reason: cannot convert from 'Student **' to 'Person *'
1>          Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>          \\cs1\cs_students\mlindahl15\cs172\final project\fpvisualstudio\main.cpp(30) : see reference to function template instantiation 'void addPerson<Student*>(std::vector<_Ty>)' being compiled
1>          with
1>          [
1>              _Ty=Person *
1>          ]
1>\\cs1\cs_students\mlindahl15\cs172\final project\fpvisualstudio\main.cpp(12): error C2664: 'void std::vector<_Ty>::push_back(_Ty &&)' : cannot convert parameter 1 from 'Adult **' to 'Person *&&'
1>          with
1>          [
1>              _Ty=Person *
1>          ]
1>          Reason: cannot convert from 'Adult **' to 'Person *'
1>          Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>          \\cs1\cs_students\mlindahl15\cs172\final project\fpvisualstudio\main.cpp(34) : see reference to function template instantiation 'void addPerson<Adult*>(std::vector<_Ty>)' being compiled
1>          with
1>          [
1>              _Ty=Person *
1>          ]
1>
1>Build FAILED.
1>
1>Time Elapsed 00:00:02.90
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

I believe that it is telling me that I have too many pointers in my code. However I don't understand why this would be the case. The only places I have pointers are saying that the vector contains which I need to repeat in each case. And these pointers are needed so I don't have object slicing.

Person.h

#ifndef PERSON_H
#define PERSON_H
#include <string>
#include "Date.h"
#include "PhoneNumber.h"
using namespace std;

class Person
{
protected:
    string name;
    Date birthday;
    PhoneNumber phoneNumber;
    string city, state;
public:
    // Constructors
    Person();
    Person(string name) { 
        this->name = name; 
        birthday = Date();
        phoneNumber = PhoneNumber("0000000000");
        city = "Unknown";
        state = "Unknown";
    }
    // Getter and Setter Methods
    string getName();
    void setName(string);
    Date getBirthday();
    // Methods
    void coutPerson();
};
#endif

Person.cpp

#include "Person.h"
#include "PhoneNumber.h"
#include "Date.h"
#include <string>
#include <iostream>
using namespace std;

// Constructors
Person::Person() {
    cin.ignore();
    cout << "Name? ";
    getline(cin, name);
    cout << "Birthday: " << endl;
    birthday.askUserForDate();
    phoneNumber.create();
    cout << "City? ";
    getline(cin, city);
    cout << "State? ";
    getline(cin, state);
}

// Getter and Setter Methods
string Person::getName() {
    return name;
}

void Person::setName(string name) {
    this->name = name;
}

void Person::coutPerson() {
    cout << name << endl;
    birthday.coutDate();
    phoneNumber.coutPhoneNumber();
    cout << city << ", " << state << endl;
}

// Methods

Student.h

#ifndef STUDENT_H
#define STUDENT_H
#include "Person.h"
using namespace std;

class Student : public Person
{
private:
    string dorm;
    int dormRoom;
public:
    // Constructors
    Student();
    Student(Student &);
    // Getter and Setter Methods

    // Methods
    void coutPerson();
};
#endif

Student.cpp

#include "Student.h"
#include "Person.h"
#include <string>
#include <iostream>
using namespace std;

// Constructors
Student::Student() {
    cin.ignore();
    cout << "Dorm? ";
    getline(cin, dorm);
    cout << "Dorm Room #? ";
    cin >> dormRoom;
}

// TODO Copy Constructors
// TODO Deconstructors
// Overload < > ==

Student::Student(Student &student) {
    Person::Person(student);
    dorm = student.dorm;
    dormRoom = student.dormRoom;
}

void Student::coutPerson() {
    cout << "DEBUG: Student::coutPerson()" << endl;
    Person::coutPerson();
    cout << "Dorm Room: " << this->dorm << " " << this->dormRoom << endl;
}

// Methods

Upvotes: 2

Views: 2433

Answers (3)

jv42
jv42

Reputation: 8583

You need to remove the * in your template parameters.

For instance:

addPerson<Professor *>(personVector);

Replace by:

addPerson<Professor>(personVector);

Currently, you're making it create a Professor **.


Update: and in your main(), you should create the persons using new Person("Name"), what you're using right now doesn't make sense, ie 'take the adress of this type' is working, but is a very bad idea (taking the address of an object created on the stack).

For instance:

personVector.push_back(&Person("James"));

Replace by:

personVector.push_back(new Person("James"));

Update2: please note that you're responsible for freeing the memory you're allocating that way at some point.

Upvotes: 2

Thomas
Thomas

Reputation: 88707

Your template parameters are pointers but shouldn't since in your methods, e.g. template<typename T> void addPerson(...) you create a new instance of T using new and thus you create a new pointer to a person (i.e. new Person*() which will result in a Person***).

Thus, just call it like this: addPerson<Professor>(personVector); which will transform new T() into new Professor() and thus yields a Professor* result.

Another thing:

void deleteElementFromArray(int element, vector<Person *> personVector) {
  vector<Person *> newPersonVector;
  for (unsigned i = 0; personVector.size() > i; i++)
    if (i != element - 1)
        newPersonVector.push_back(personVector[i]);
    personVector = newPersonVector;
}

First, you're removing element - 1 so be aware that you can't pass 0 (i.e. element is a one-based index here).

Second, you assign newPersonVector to the parameter personVector. However, since you're passing the vector by value (it is copied) that change won't be visible outside that function.

You have several solutions to this:

  1. delete the item from personVector rather than copying those that should not be deleted. Use something like personVector.erase(personVector.begin() + element) for that (note that the syntax here might not be quite right, but you should get it).
  2. pass a pointer to the vector: void deleteElementFromArray(int element, vector<Person *>* personVector)
  3. return the new vector and replace the old vector. The call thus becomes personVector = removeFriend(personVector);

I'd personally prefer option 1 since it reuses the vector and thus reduces copy operations.

Upvotes: 2

mark mao
mark mao

Reputation: 105

the code

addPerson<Professor *>(personVector); 

try to modify as:

addPerson<Professor>(personVector); 

Upvotes: 1

Related Questions