Reputation: 538
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
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
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