Хранение разнородного типа класса в переменной - PullRequest
3 голосов
/ 03 мая 2020

Я начинающий с c ++ :), я упростил свою задачу до этого:

У меня есть класс Person

class Person{
    string name;}

И еще один класс Student, который наследует Person

class Student : public Person{
    string schoolName;}

И затем у меня есть библиотека классов, которая должна иметь различный компилятор в зависимости от ввода

class Library{
   void checkBeforeEntry(Person* p){
       cout << "You must be a student to enter" << endl;}
   void checkBeforeEntry(Student* s){
       cout << "You can enter" << endl;}}

В какой-то момент в моей программе я храню такого человека: Person* guy; потому что в этот момент я еще не знаю, простой ли он человек или ученик.

Но из-за этого я «теряю» информацию о реальном типе объекта, и я попробовал это :

    Person *p = new Person();
    Student *s = new Student();
    Person *g = new Student();

    lib.checkBeforeEntry(p);
    lib.checkBeforeEntry(s);
    lib.checkBeforeEntry(g);

Вывод был:

You must be a student to enter
You can enter
You must be a student to enter

Но я хочу, чтобы последнее утверждение было «вы можете ввести». Я использовал немного полиморфизма прежде, чтобы адаптировать компиляцию производного класса, но здесь я пытаюсь изменить компиляцию стороннего класса (библиотеки), чтобы ситуация была не такой, как я привык. Вы знаете, как решить эту проблему или мне следует использовать другой дизайн?

Ответы [ 3 ]

3 голосов
/ 03 мая 2020

Вы можете использовать dynamic_cast для приведения вниз, который будет успешным (т.е. вернет не nullptr значение), если тип времени выполнения правильный.

Применимо к вашей ситуации:

void checkBeforeEntry(Person* p){
    if (dynamic_cast<Student*>(p)) {
       std::cout << "You can enter" << std::endl;
    } else {
       std::cout << "Only students can enter" << std::endl;
    }
}

У этого недостатка, однако, является то, что он требует, чтобы информация о типе времени выполнения (RTTI) была включена, и это может быть довольно дорого (поскольку обычные реализации ищут и проверяют информацию RTTI, хранящуюся прямо рядом с vftable). Кроме того, он требует, чтобы вы указали определенные c типы (представьте, как добавление Professor, Librarian, et c. Классы раздули бы приведенный выше код)

Возможно, лучшим подходом может быть объявление virtual bool isStudent() или virtual bool canEnterLibrary() на Person

2 голосов
/ 03 мая 2020

То, что вы здесь используете, называется стати c -полиморфизмом. Это означает, что проверка типа выполняется во время компиляции, а не во время выполнения. Т.е. тип указателей определяет тип объектов и, следовательно, версию перегруженной функции checkBeforeEntry(), которая будет вызвана.

Для достижения того, что вы хотите, и самым простым способом с минимальным воздействием на из имеющегося у вас кода, я думаю, что следующее может быть лучшим:

class Person {
public:
    string name;
    virtual bool passLibrary() const {return false;}
    virtual ~Person();
};

class Student: public Person {
public:
    string schoolName;
    bool passLibrary() const override {return true;}
    virtual ~Student();
};

class Library {
public:
    void checkBeforeEntry(const Person *p) const {
        if (p->passLibrary()) {
            cout << "You can enter" << endl;
        } else {
            cout << "You must be a student to enter" << endl;
        }
    }
};

Так что здесь функция passLibrary() является виртуальной функцией, что означает, что она будет использовать динамический c -полиморфизм, т.е. проверять текущий тип объекта во время выполнения, и каждый из ваших классов Person и Student предоставит свою собственную реализацию, которая будет использоваться библиотекой класса для различения guish между объектами Person и Student.

Надеюсь, это было достаточно ясно.

1 голос
/ 03 мая 2020
class Person {
    string name;
  public:
    virtual ~Person() = default;
    virtual bool isStudent() const { return false; }
};

class Student : public Person {
    string schoolName;
  public:
    bool isStudent() const override { return true; }
};

class Library {
    void checkBeforeEntry(Person* p) {
       if (!p->isStudent())
           cout << "You must be a student to enter" << endl;
       else
           cout << "You can enter" << endl;
    }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...