Raph Schim
Raph Schim

Reputation: 538

Convert parent class to child during execution

I have a parent class named SavableFile and 2 class SaveA and SaveB inherited from SavableFile.

They are defined like this :

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; 
   }
}

My question is: would it be possible to create a SavableFile and using the extension of the filename, convert this savableFile to saveA or saveB? Something like

 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);
     }
}

So that I could do something like 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"
}

I could always make the test in the main to check if I have to create a SaveA or a SaveB class, but I think it would be a less good approach of the problem. But I can't find a way to do that, and I'm not finding the good wording in order to find help somewhere... Is it possible? Thanks a lot!

Upvotes: 0

Views: 72

Answers (2)

Ugnius Malūkas
Ugnius Malūkas

Reputation: 2827

You can do this by using Factory Method design pattern:

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;
    }
};

Full code:

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;
}

Upvotes: 1

Thomas
Thomas

Reputation: 181745

No, you cannot convert an existing object to a different type.

An alternative approach would be the named constructor pattern:

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);
     }
}

This does have the drawback of always returning a dynamically allocated pointer. To get around that, you could add another layer of abstraction: SavableFile would contain a private FileSaver *p_filesaver, where FileSaver is an interface containing just the virtual void write() function.

Upvotes: 1

Related Questions