Преобразование родительского класса в дочерний во время выполнения - PullRequest
0 голосов
/ 02 июля 2019

У меня есть родительский класс с именем SavableFile, 2 класса SaveA и SaveB, унаследованные от SavableFile.

Они определены так:

class SavableFile {
    SavableFile(std::string _filename) : p_filename(_filename){}
    virtual void write() = 0;
protected:
    std::string p_filename;
}

class SaveA : public SavableFile {
   SaveA(std::string _filename) : SavableFile(_filename) {}
   void write() {
        std::cout << "A" << std::endl; 
   }
}

class SaveB : public SavableFile {
   SaveB(std::string _filename) : SavableFile(_filename) {}
   void write() {
        std::cout << "B" << std::endl; 
   }
}

Мой вопрос: возможно ли создать SavableFile и, используя расширение имени файла, преобразовать этот savableFile в saveA или saveB? Что-то вроде

 SavableFile(std::string _filename) : p_filename(_filename){
     std::string ext = GetExtension(_filename);   
     //this is purely fictionnal, it's only in order to give an idea, i'm not sure if there is a way to do that, that's why i'm asking
     if (ext.lower() == "a"){
          *this = dynamic_cast<SaveA>(*this);
     } 
     else {
          *this = dynamic_cast<SaveB>(*this);
     }
}

Чтобы я мог сделать что-то вроде этого:

int main(int argc, char* argv[]){
    SavableFile svA("foo.a");
    //Here, SavableFile has been changed into SaveA class because of the constructor of SavableFile
    svA->write();
    //Here should be writed "A"
    SavableFile svB("bar.b");
    svB->write();
    //Here should be writed "B"
}

Я всегда мог сделать тест в основном, чтобы проверить, нужно ли мне создавать класс SaveA или SaveB, но я думаю, что это был бы менее удачный подход к проблеме. Но я не могу найти способ сделать это, и я не нахожу хорошую формулировку, чтобы где-то найти помощь ... Является ли это возможным? Большое спасибо!

Ответы [ 2 ]

1 голос
/ 02 июля 2019

Вы можете сделать это, используя Заводской метод Шаблон проектирования:

class Factory {
public:
    static SavableFile* get(std::string _filename) {
        std::string ext = GetExtension(_filename);
        if (ext.lower() == "a") {
            return new SaveA(_filename);
        } 
        else {
          return new SaveB(_filename);
        }
        return nullptr;
    }
};

Полный код:

class SavableFile {
public:
    SavableFile(std::string _filename) : p_filename(_filename) {}
    virtual void write() = 0;
protected:
    std::string p_filename;
};

class SaveA : public SavableFile {
public:
    SaveA(std::string _filename) : SavableFile(_filename) {}
    void write() override {
        std::cout << "A" << std::endl; 
    }
};

class SaveB : public SavableFile {
public:
   SaveB(std::string _filename) : SavableFile(_filename) {}
   void write() override {
        std::cout << "B" << std::endl; 
   }
};

class Factory {
public:
    static SavableFile* get(std::string _filename) {
        std::string ext = GetExtension(_filename);
        if (ext.lower() == "a") {
            return new SaveA(_filename);
        } 
        else {
            return new SaveB(_filename);
        }
        return nullptr;
    }
};

int main(int argc, char* argv[]) {
    SavableFile* svA = Factory::get("foo.a");
    // It is better to check here if the pointer is not null.
    svA->write();
    SavableFile* svB = Factory::get("bar.b");
    // It is better to check here if the pointer is not null.
    svB->write();

    delete svA;
    delete svB;
    return 0;
}
1 голос
/ 02 июля 2019

Нет, вы не можете преобразовать существующий объект в другой тип.

Альтернативным подходом будет именованный шаблон конструктора :

class SavableFile {
  public:
    static SavableFile *create(std::string _filename); // named constructor
    virtual void write() = 0;
  protected:
    SavableFile(std::string _filename) : p_filename(_filename){} // actual constructor now protected
    std::string p_filename;
};

SavableFile *SavableFile::create(std::string _filename) {
     std::string ext = GetExtension(_filename);   
     if (ext.lower() == "a"){
          return new SaveA(_filename);
     } 
     else {
          return new SaveB(_filename);
     }
}

Это действительно имеетнедостаток всегда возвращает динамически размещенный указатель.Чтобы обойти это, вы можете добавить еще один уровень абстракции: SavableFile будет содержать private FileSaver *p_filesaver, где FileSaver - это интерфейс, содержащий только функцию virtual void write().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...