0

I have an abstract class "Mark" and it has a child class "Int_num". I also have a "Subject" class. I want a pointer to the address in the memory of the "Mark" class to be written to the "mark" parameter when calling its constructor. What should I do to make the mark pointer point to the "Mark" class?" occurred, after the compiler complaint about "expression must have class type" or something like that in mark.print_mark()?

class Mark {
private:
    int mark;
public:
    virtual void change_mark(int);
    virtual void print_mark();
    virtual int return_mark();
};

class Int_mark : public Mark {
private:
    int mark;
public:
    Int_mark();
    Int_mark(int);
    ~Int_mark();

    void change_mark(int = 0);
    void print_mark() const;
    int return_mark() const;
};

Int_mark::Int_mark() {
    std::string str_mark;
    std::cout << "New mark: ";
    std::cin.ignore();
    std::getline(std::cin, str_mark);

    str_mark = ltrim(rtrim(str_mark));
    int new_mark;
    try {
        new_mark = stoi(str_mark);
    } catch(...) {
        std::cout <<"wq";
        mark = 1;
        return ;
    }

    try {
        if((new_mark < 1) || (new_mark > 5))
            throw 1;
        else
            mark = new_mark;
    } catch(int a) {
        std::cout << "qw" << std::endl;
        mark = 1;
    }
}
void Int_mark::print_mark() const {
    std::cout << "Mark: " << mark << std::endl;
}

Subject

#include "Mark.h"
#include <string>
#include <vector>

class Subject {
private:
    std::string name_subject;
    std::string type_subject;
    unsigned hour_subject = 0;
    void *mark = nullptr;
public:
    Subject();
    Subject(std::string, int);
    Subject(std::string, bool);
    ~Subject();

    void change_mark(unsigned);
    void change_mark(bool);
    void rename_subj(std::string);
    void add_hour(unsigned);
};

Subject::Subject() {
    std::string name_sub;
    std::cout << "Введите название предмета: ";
    getline(std::cin, name_sub);
    name_sub = split_string(name_sub);
    name_subject = name_sub;
    int select = 2;

    if(select == 1) {
        type_subject = "Bool";
        //mark = new Bool_mark();
    } else {
        type_subject = "Int";    
        mark = new Int_mark();   


//What should I do to make the mark pointer point to the "Mark" class?
        mark.print_mark();

}
}

main

#include "subject/Subject.h"

using namespace std;

int main() {
    Subject q;
}

What am I doing wrong? How should I do this?

Froggy
  • 13
  • 6
  • 7
    *In the "Mark" class, everything works, but everything works.* What is the question? And what is the problem? – super Feb 21 '20 at 10:52
  • Why are there TWO `mark` members in `Int_mark` ? I think you should check the " inheritance" chapter in your C++ book – MSalters Feb 21 '20 at 10:55
  • Related: [Why does std::getline() skip input after a formatted extraction?](https://stackoverflow.com/questions/21567291/why-does-stdgetline-skip-input-after-a-formatted-extraction), but we don't know what your actual problem is. – Yksisarvinen Feb 21 '20 at 11:03
  • I think it doesn't even compile. ` mark.print_mark();` must be ` mark->print_mark();`, no? – churill Feb 21 '20 at 11:33
  • A pointer doesn't point to a class but to a object of a class. In your case the pointer points to a new object. Therefore the question is unclear for me. – Thomas Sablik Feb 21 '20 at 11:33
  • @ThomasSablik I think the question "What should I do to make the mark pointer point to the "Mark" class?" occurred, after the compiler complaint about "expression must have class type" or something like that in `mark.print_mark()` – churill Feb 21 '20 at 11:43
  • @ThomasSablik Thanks. I can't explain it properly, because I speak Mongolian – Froggy Feb 21 '20 at 11:50

2 Answers2

1

The pointer mark is of type void *. You could cast it with

static_cast<Int_mark*>(mark)

and call the function with

static_cast<Int_mark*>(mark)->print_mark();

But usually in OOP mark would be a pointer to the base class

Mark *mark = nullptr;

Now you can check for errors with

mark = new Int_mark();   

auto *m = dynamic_cast<Int_mark*>(mark);

if (m)
    m->print_mark();

Remember the virtual destructor in the base class

virtual ~Mark();

When to use virtual destructors?

Here is a fixed version of your code:

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

class Mark {
public:
    virtual ~Mark() = default;

    //virtual void change_mark(int) = 0;
    virtual void print_mark() const = 0;
    //virtual int return_mark() const = 0;
};

class Int_mark : public Mark {
private:
    int mark;
public:
    Int_mark();
    Int_mark(int);
    ~Int_mark() override = default;

    //void change_mark(int = 0) override;
    void print_mark() const override;
    //int return_mark() const override;
};

Int_mark::Int_mark() {
    std::string str_mark;
    std::cout << "New mark: ";
    std::cin.ignore();
    std::getline(std::cin, str_mark);

    //str_mark = ltrim(rtrim(str_mark));
    int new_mark;
    try {
        new_mark = stoi(str_mark);
    } catch(...) {
        std::cout <<"wq";
        mark = 1;
        return ;
    }

    try {
        if((new_mark < 1) || (new_mark > 5))
            throw 1;
        else
            mark = new_mark;
    } catch(int a) {
        std::cout << "qw" << std::endl;
        mark = 1;
    }
}
void Int_mark::print_mark() const {
    std::cout << "Mark: " << mark << std::endl;
}

class Subject {
private:
    std::string name_subject;
    std::string type_subject;
    unsigned hour_subject = 0;
    Mark *mark = nullptr;
public:
    Subject();
    Subject(std::string, int);
    Subject(std::string, bool);
    ~Subject();

    void change_mark(unsigned);
    void change_mark(bool);
    void rename_subj(std::string);
    void add_hour(unsigned);
};

Subject::Subject() {
    std::string name_sub;
    std::cout << "Введите название предмета: ";
    getline(std::cin, name_sub);
    //name_sub = split_string(name_sub);
    name_subject = name_sub;
    int select = 2;

    if(select == 1) {
        type_subject = "Bool";
        //mark = new Bool_mark();
    } else {
        type_subject = "Int";    
        mark = new Int_mark();   

        auto *m = dynamic_cast<Int_mark*>(mark);
        if (m)
            m->print_mark();
    }
}

Subject::~Subject() {
    delete mark;
}

int main() {
    Subject q;
}
Thomas Sablik
  • 15,040
  • 7
  • 26
  • 51
  • `m->print_mark()` still calls `Int_mark::print_mark()` but I think he wants to call the base class version of `print_mark()` here – Odysseus Feb 21 '20 at 14:43
  • @Odysseus _"I have an abstract class "Mark" and it has a child class "Int_num"."_ OP marked `print_mark()` as `virtual` to call the child's implementation through the base class. Therefore there is no implementation of `Mark::print_mark`. – Thomas Sablik Feb 21 '20 at 14:46
  • Don't get me wrong, I actually like your solution but it does not answer the OP's question how to access base class part by derived class object – Odysseus Feb 21 '20 at 15:06
  • @Odysseus There is no `Mark::print_mark();` declared so it's obvious that OP doesn't want to call that function. OP also wrote _"Thanks. I can't explain it properly, because I speak Mongolian"_. That seems to be a language barrier. OP wants to use polymorphism but didn't declare `mark` as `Mark *mark;` but as `void *mark;` – Thomas Sablik Feb 21 '20 at 15:09
  • There is indeed some further clarification needed here - I interpret _"What should I do to make the mark pointer point to the "Mark" class? `mark.print_mark();`"_ so that he wants exactly to do that and will for sure run into the error that `Mark::print_mark()` is not defined as soon as he properly calls it – Odysseus Feb 21 '20 at 15:13
  • @Thomas Sablik Thank you for your help. Sorry for not quite the correct question. – Froggy Feb 21 '20 at 21:25
0

Since I did not correctly understand the question in the first place, here a way how you can call the member function of base class Mark by object of derived class Int_Mark:

Int_mark *mark = new Int_mark();
mark->print_mark();       // calls member of the class Int_mark
mark->Mark::print_mark(); // calls member of the class Mark

Make sure that Mark::print_mark() is also defined and not just Int_mark::print_mark()

Odysseus
  • 805
  • 2
  • 11
  • There are setters and getters for mark: `change_mark`, `print_mark` and `return_mark` – Thomas Sablik Feb 21 '20 at 11:35
  • @ThomasSablik True - than he can for sure just use `change_mark` – Odysseus Feb 21 '20 at 12:30
  • 1
    OP doesn't want to change the mark. The question is how to print the mark through polymorphism. – Thomas Sablik Feb 21 '20 at 12:32
  • Well I got it wrong and thought he refers to constructor of `Int_Mark` but the issue is the same, just the the print function instead of the set function – Odysseus Feb 21 '20 at 12:48
  • I don't understand how this answer could help someone. The first part is to implement and use a getter but OP already uses a getter. The second part is to remove an unused variable. Both parts won't fix the problem. – Thomas Sablik Feb 21 '20 at 13:29
  • Yes he uses `Int_Mark::print_mark` instead of `Mark::print_mark` - but the latter one seems not be defined at all – Odysseus Feb 21 '20 at 13:31
  • No, it's `void *mark;`. OP uses `void::print_mark` and that's the problem – Thomas Sablik Feb 21 '20 at 13:40
  • It makes no sense for the compiler, too. That's the reason OP asked this question. This code can't be compiled because `mark` is of type `void *` and OP tries to call `print_mark` on it. And that's the reason this answer makes no sense. It doesn't even address the problem, let alone fixing it. – Thomas Sablik Feb 21 '20 at 14:14
  • No his question was how he could get the base class part of its object and not why this does not compile – Odysseus Feb 21 '20 at 14:17
  • _"What should I do to make the mark pointer point to the "Mark" class?" occurred, after the compiler complaint about "expression must have class type" or something like that in mark.print_mark()? ... What am I doing wrong? How should I do this?"_ OP mentioned that this code can't be compiled and asked how to fix it – Thomas Sablik Feb 21 '20 at 14:19
  • And you focused on how to make his current code compilable, I focused on the question _"What should I do to make the mark pointer point to the "Mark" class?"_ – Odysseus Feb 21 '20 at 14:41