1

I am currently learning c++ and i am now working on Inheritance. I have to make a regular question class as well as a derived class that is a Numeric question and a multiple choice question. I make the questions in the code, and then display them 1 by 1 to the user. The user then answers the questions, and the program is supposed to check whether the answer is correct.

#include <iostream>
#include <string>
using namespace std;

class Question {
protected:
    string text;
    string answer;
    string input;
public:
    Question(string inText, string inAnswer) {
        text = inText;
        answer = inAnswer;
    }
    Question() {
        text = "blank question";
        answer = " ";
    }
    void setQuestion(string txt) {
        text = txt;
    }
    void setAnswer(string answr){
        answer = answr;
    }
    void userAnswer(string ans) {
        input = ans;
    }
    string getAnswer() {
        return answer;
    }
    string getQuestion() {
        return text;
    }

    void displayQuestion() {
        cout << getQuestion() << endl;
    }
    void isCorrect() {
        cout << "default function" << endl;
        if (input.compare(answer) == 0)
            cout << "True" << endl;
        else
            cout << "False" << endl;
    }
};
class NumericQuestion : public Question {
protected:
    double ans;
    double inp;
public:
    NumericQuestion(string inText, double inAns) {
        text = inText;
        ans = inAns;
    }
    void userAnswer(string ans) {
        inp = stod(ans);
    }

    void isCorrect() {
        cout << "numeric function" << endl;
        if (inp == ans)
            cout << "True" << endl;
        else if ((inp - ans) <= 0.01)
        cout << "False" << endl;
        else
            cout << "False" << endl;
}
};
class MultipleChoice : public Question {
protected:
    string qA, qB, qC, qD;
public:
    MultipleChoice(string inText, string qA, string aB, string qC, string qD,         char inAnswer) {
        text = inText;
        answer = inAnswer;
    }
    void displayQuestion() {
        cout << text << endl;
        cout << "a) " << qA << "    " << "b) " << qB << endl;
        cout << "c) " << qC << "    " << "d) " << qD << endl;
    }
};

int main() {
    string ans;

    Question q1("whats 2+2", "four");
    NumericQuestion q2("2+2", 4);

    MultipleChoice q3("The Right Answer is C", "answer A", "thisisB", "thats C", "Wrong", 'c');

    Question arr[] = { q1,q2,q3};
    for (int i = 0; i < 3; i++) {
        arr[i].displayQuestion();
        cin >> ans;
        arr[i].userAnswer(ans);
        arr[i].isCorrect();
    }

getchar();
return 0;
}

The member function isCorrect() from NumericQuestion class and displayQuestion() from MultipleChoice class don't get used, instead, the ones from Question class get used, which lead to logical errors in my code.

Gleb
  • 107
  • 1
  • 9

3 Answers3

2

You're slicing objects when you assign subclasses of Questions by value to an array of Question when you do Question arr[] = { q1, q2, q3 };. This means that even though some of your derived Question objects have extra members that the base class doesn't, they're being truncated away by the assignment into the array. Another problem is that because arr is declared to hold plain and simple Question objects, the compiler will assume that a call like arr[i].isCorrect(); will always refer to Question::isCorrect(), and not a derived method. Here's a couple things to fix.

Make overridable functions virtual:

class Question {
    ...
    virtual void isCorrect() {
        cout << "default function" << endl;
        if (input.compare(answer) == 0)
            cout << "True" << endl;
        else
            cout << "False" << endl;
    }

and override them in subclasses:

class NumericQuestion : public Question {
    ...
    void isCorrect() override {
        cout << "numeric function" << endl;
        if (inp == ans)
            cout << "True" << endl;
        else if ((inp - ans) <= 0.01)
            cout << "False" << endl;
        else
            cout << "False" << endl;
    }

Finally, avoid the slicing by storing base class pointers to your Questions. Here, I'm using std::shared_ptr to avoid the headaches of manual memory management. Also appearing is a ranged-for loop:

auto q1 = std::make_shared<Question>("whats 2+2", "four");
auto q2 = std::make_shared<NumericQuestion> q2("2+2", 4);

auto q3 = std::make_shared<MultipleChoice>("The Right Answer is C", "answer A", "thisisB", "thats C", "Wrong", 'c');

// even though q1...q3 are shared_ptrs to derived classes, they can be safely cast to shared_ptrs to the base class
std::vector<std::shared_ptr<Question>> questions { q1, q2, q3 };

for (const auto& question: questions) {
    question->displayQuestion();
    cin >> ans;
    question->userAnswer(ans);
    question->isCorrect();
}
alter_igel
  • 6,899
  • 3
  • 21
  • 40
  • I'd recommend a `std::unique_ptr` instead of a `std::shared_ptr` unless you really need shared ownership. Otherwise you are paying for reference counting you will never use. – NathanOliver Aug 08 '18 at 19:09
  • @NathanOliver I considered that, but it would mean `std::move`-ing them into the vector, and I'm not sure OP would be ready for that – alter_igel Aug 08 '18 at 19:10
  • @NathanOliver at the current level the asker is, the cost of reference counting is certainly negligible. IMO, it's even too early to introduce shared pointers. It's a very nice answer but you may be dropping fancy modern C++ that his class professor might not even know about. – Jeffrey Aug 08 '18 at 20:04
0

There are two issues at play here.

1) Based on your for loop at the end, you need to declare some functions, like displayQuestion(), userAnswer(), and isCorrect() as virtual.

2) Secondly, change your arr declaration to Question *arr[] = {&q1, &q2, &q3};.

meat
  • 609
  • 3
  • 8
0

You need to set userAnswer, displayQuestion and isCorrect as virtual in the base class.

You also need to store your question as pointers (there's other options) to prevent slicing. Personally, I find it simpler to fix it like this:

Question* arr[] = {&q1,&q2,&q3};
...
arr[i]->displayQuestion();

(you need to change all usage or arr[i] to use arrows)

Jeffrey
  • 11,063
  • 1
  • 21
  • 42