-1

I'm trying to overload operator<< in several subclasses. I have a superclass called Question, which has an enumerated value type, and a string question. The subclasses of this class are TextQuestion, ChoiceQuestion, BoolQuestion and ScaleQuestion. TextQuestion has no additional data fields. ChoiceQuestion has a vector of strings, to store the multiple choice possibilities. BoolQuestion has no additional data fields. ScaleQuestion has two int-values, low_ and high_, for the scale.

class Question {
public:
    enum Type{TEXT, CHOICE, BOOL, SCALE};
    Question():
        type_(), question_() {}
    Question(Type type, std::string question):
        type_(type), question_(question) {}
    friend std::ostream& operator<<(std::ostream& out, const Question& q);
    virtual void print(std::ostream& out) const;
    virtual ~Question();
    private:
    Type type_;
    std::string question_;
};

class TextQuestion: public Question {
public:
    TextQuestion():
        Question() {}
    TextQuestion(Type type, std::string question):
        Question(type, question) {}
    void print(std::ostream& out) const;
    virtual ~TextQuestion();
};

class ChoiceQuestion: public Question {
public:
    ChoiceQuestion():
        Question(), choices_() {}
    ChoiceQuestion(Type type, std::string question, std::vector<std::string> choices):
        Question(type, question), choices_(choices) {}
    void print(std::ostream& out) const;
    virtual ~ChoiceQuestion();
private:
    std::vector<std::string> choices_;
};

class BoolQuestion: public Question {
public:
    BoolQuestion():
        Question() {}
    BoolQuestion(Type type, std::string question):
        Question(type, question) {}
    void print(std::ostream& out) const;
    virtual ~BoolQuestion();
};

class ScaleQuestion: public Question {
public:
    ScaleQuestion():
        Question(), low_(), high_() {}
    ScaleQuestion(Type type, std::string question, int low = 0, int high = 0):
        Question(type, question), low_(low), high_(high) {}
    void print(std::ostream& out) const;
    virtual ~ScaleQuestion();
private:
    int low_, high_;
};

Now, I'm trying to overload operator<< for all these subclasses and I tried using this example

So I made a virtual print function in the superclass, overloaded the print function in each subclass and the operator<< in the superclass calls the print-function.

std::ostream& operator<<(std::ostream& out, const Question& q) {
q.print(out);
return out;
}

void Question::print(std::ostream& out) const {
    std::string type;
    switch(type_) {
    case Question::TEXT:
        type = "TEXT";
        break;
    case Question::CHOICE:
        type = "CHOICE";
        break;
    case Question::BOOL:
        type = "BOOL";
        break;
    case Question::SCALE:
        type = "SCALE";
        break;
    }
    out << type << " " << question_;
}

void TextQuestion::print(std::ostream& out) const {
    Question::print(out);
}

void ChoiceQuestion::print(std::ostream& out) const {
    Question::print(out);
    out << std::endl;
    int size(get_choices_size());
    for (int i = 0; i < size; ++i) {
        out << choices_[i] << std::endl;
    }
}

void BoolQuestion::print(std::ostream& out) const {
    Question::print(out);
   }

void ScaleQuestion::print(std::ostream& out) const {
    Question::print(out);
        out << " " << low_ << " " << high_;
}  

I did it exactly like in the example, but when I output my Questions, it always uses the baseclass and outputs only the type and the question. The compiler never uses the subclasses.

Community
  • 1
  • 1
JNevens
  • 11,202
  • 9
  • 46
  • 72
  • print is not virtual - why? – Till Dec 02 '13 at 10:11
  • 1
    @Till it is (look at the decl in `Question`) – WhozCraig Dec 02 '13 at 10:12
  • 1
    Show us the implementation of the print methods and the << operators. – mcserep Dec 02 '13 at 10:12
  • 1
    Terminology: **Overload** means having two simultaneously accessible functions with the same name, and automatic compile-time selection based on argument types. **Override** means having a group of functions with identical argument types, and one is selected by the dynamic type of the "acting" object. – Potatoswatter Dec 02 '13 at 10:14
  • @Potatoswatter: I think it was just a typo in the question talking about overloading the print method. – mcserep Dec 02 '13 at 10:16
  • 2
    I have a often-overused crystal-ball that is telling me somewhere along the line you're object-slicing your derived types down to `Question` before sending them to the output iterator. Can you please post the code for the insertion operator and your *real* usage of it? Pretty-please ? – WhozCraig Dec 02 '13 at 10:18
  • 1
    @Potatoswatter: I think you made the question even more incorrect, than it was before. You cannot override the << operator, only overload it. – mcserep Dec 02 '13 at 10:18
  • @WhozCraig i've added the code of the print functions – JNevens Dec 02 '13 at 10:20
  • @CMate If he could override `operator <<`, there wouldn't be a question in the first place. The title and question should reflect what the asker wants, so as to be easily found by future askers. – Potatoswatter Dec 02 '13 at 10:21
  • 1
    @JN11 Thanks, now the last thing. Show where you're actually sending your `Question` derivations to the output stream. I've still my suspicion you're stacking up your derivations in an array, vector, or otherwise sublime container of `Question`, which would facilitate the slicing I was referring to earlier. Please show how the objects your sending through your operator are defined, and the code which sends them. Thx. – WhozCraig Dec 02 '13 at 10:23
  • @Potatoswatter: I don't think definitions should be handled so lightly. We shouldn't call an actual overload an override, just because "imitating" an override is the goal of the question. – mcserep Dec 02 '13 at 10:23
  • 1
    In the edit how is `type_` determined? It only appears in one place, where it's being used. –  Dec 02 '13 at 10:24
  • Oh, actually you do already have a virtual function. The most likely problem is object slicing. What is the type of the object used with `operator <<` in the misbehaving test code? – Potatoswatter Dec 02 '13 at 10:25
  • 1
    @WhozCraig I have a class called Memory, which saves all the Questions in a vector. I overloaded the operator<< of the Memory class, so it would go through the vector of Questions, and output each Question (Using the operator<< of Question). I use this overloaded operator<< of the Memory class when I want to output the entire Memory to a file. – JNevens Dec 02 '13 at 10:29
  • 1
    If `Memory` saves them in a `std::vector` there is where your object slicing is happening. Is that what you have? (stick with this, I think we're almost through). – WhozCraig Dec 02 '13 at 10:30
  • 1
    Why is a user allowed to set the wrong type on a question? Use `BoolQuestion(std::string question): Question(BOOL, question) {}` to guard against it. – molbdnilo Dec 02 '13 at 10:32
  • @WhozCraig Yes, that's it! But I've never heard of object slicing? What exactly is it? And how can I solve it? Does my Memory class has to become a template class?? – JNevens Dec 02 '13 at 10:33
  • @JN11 Use references or their cousins, pointers and smart pointers. – Potatoswatter Dec 02 '13 at 10:34
  • 1
    First, read potatoswatter's answer. Then, hit the search box at the top-right corner of this page and type "[c++] what is object slicing", [See this question](http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c/14461532#14461532) – WhozCraig Dec 02 '13 at 10:35
  • @Potatoswatter I'm using pointers now, but it still won't work – JNevens Dec 02 '13 at 11:28
  • @WhozCraig Do I have to use a vector of Questions, and use pointers in the operator<< of Memory? Or use a vector of Question* and use them in the opertator<< of Memory?? – JNevens Dec 02 '13 at 11:35

2 Answers2

8
  1. Only virtual functions allow for dispatch according to the dynamic type of an object (given the static type of a base class).

  2. Only nonstatic member functions can be virtual.

  3. operator << cannot be virtual because it cannot be a nonstatic member function, because its first argument must be the stream.

  4. You can call a virtual function from an operator << which accepts a reference to the base class.

    Calling a virtual function from a non-virtual function, to preserve interface aspects which are not overridden, is called the non-virtual idiom.

Ah, I did not see any virtual upon first read but now I do. If you are not getting subclass behavior where expected, a likely culprit is slicing, where you create an object of base class type and assign it a value from a subclass object.

TextQuestion q( "What is hello, world?" ); // Original object
Question & qr( q ); // Reference, not another object
Question q2( q ); // Base class object with copied subset (slice) of data.

std::cout << q << '\n'; // Observe subclass behavior.
std::cout << qr << '\n'; // Observe subclass behavior due to dynamic typing.
std::cout << q2 << '\n'; // Observe superclass behavior due to slicing.    
Community
  • 1
  • 1
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
1

You cannot override operator<< in your classes because it cannot be a member (you cannot pass this as the right operand).

Instead, have a virtual print function overriden in each subclass, and define global/nanespace scope operator<< in terms of it.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243