Call derived class method from base pointer list loop (OOD)












3















Problem



I ran into a simple issue, though I can't come up with a proper OOD for it.



What I have:




  • Base class

  • Subclass adding a new method foo()

  • List of pointers to the base class instances


What I need:



I need to loop through this list and call the foo() for objects supporting this method, i.e. objects of (or derived from) the aforementioned subclass. Or speaking generally, I need a "non-smelly" polymorphic access to a subclass via list of pointers to a base class.



Example Code



class Entity {
// ...
// This class contains methods also needed by subclasses.
};

class SaveableEntity : public Entity {
public:
virtual void save() = 0;
};

// SaveableEntity has multiple subclasses with specific save() implementations.

std::vector<Entity *> list;
for (Entity *entity : list) {
// Here I need to save() the descendants of a SaveableEntity type.
}


I came up with some ideas, however none of them seem right to me. Here are some of them:



Method 1: dynamic_cast



As some elements are saveable and some are not, the most obvious way I see is dynamic casting:



std::vector<Entity *> list;
for (Entity *entity : list) {
auto saveable = dynamic_cast<SaveableEntity *>(entity);
if (saveable) {
saveable->save();
}
}


However, using dynamic_cast looks like a bad OOD in this situation (correct me if I'm wrong). Also, this approach can easily lead to the violation of the LSP.



Method 2: Move save() to a base class



I could remove SaveableEntity and move the save() method to the base Entity. However, this makes us implement dummy method:



class Entity {
virtual void save() {
// Do nothing, override in subclasses
}
};


This eliminates the dynamic_cast usage, but the dummy method still doesn't seem right: now the base class holds the information (save() method) totally unrelated to it.



Method 3: Apply design patterns





  • Strategy pattern: SaveStrategy class and its subclasses like NoSaveStrategy, SomeSaveStrategy, SomeOtherSaveStrategy, etc. Again, the presence of NoSaveStrategy brings us back to the flaw of the previous method: base class has to know the particular details about its subclass, which seems like a bad design.


  • Proxy or Decorator patterns can easily encapsulate the dynamic_cast, however this will only hide the unwanted code, not get rid of the bad design itself.

  • Add some composition-over-inheritance layers, and so on and so on...


Question



Maybe I'm missing some obvious solution, or maybe the described methods (1 or 2) are not as bad and smelly in this particular context as I'm seeing them.



So what design approach is suitable in such situation?










share|improve this question

























  • you can still implement a pure virtual function. this would make it more or less a default behaviour. i am not sure about the smellyness though :-/

    – MauriceRandomNumber
    Nov 22 '18 at 15:05











  • Look at your problem under this angle: base class interface does not support foo. And you want to do via said interface (accessing real objects via base class pointers) something ( foo ) that it does not support.

    – Andrew Kashpur
    Nov 22 '18 at 15:06
















3















Problem



I ran into a simple issue, though I can't come up with a proper OOD for it.



What I have:




  • Base class

  • Subclass adding a new method foo()

  • List of pointers to the base class instances


What I need:



I need to loop through this list and call the foo() for objects supporting this method, i.e. objects of (or derived from) the aforementioned subclass. Or speaking generally, I need a "non-smelly" polymorphic access to a subclass via list of pointers to a base class.



Example Code



class Entity {
// ...
// This class contains methods also needed by subclasses.
};

class SaveableEntity : public Entity {
public:
virtual void save() = 0;
};

// SaveableEntity has multiple subclasses with specific save() implementations.

std::vector<Entity *> list;
for (Entity *entity : list) {
// Here I need to save() the descendants of a SaveableEntity type.
}


I came up with some ideas, however none of them seem right to me. Here are some of them:



Method 1: dynamic_cast



As some elements are saveable and some are not, the most obvious way I see is dynamic casting:



std::vector<Entity *> list;
for (Entity *entity : list) {
auto saveable = dynamic_cast<SaveableEntity *>(entity);
if (saveable) {
saveable->save();
}
}


However, using dynamic_cast looks like a bad OOD in this situation (correct me if I'm wrong). Also, this approach can easily lead to the violation of the LSP.



Method 2: Move save() to a base class



I could remove SaveableEntity and move the save() method to the base Entity. However, this makes us implement dummy method:



class Entity {
virtual void save() {
// Do nothing, override in subclasses
}
};


This eliminates the dynamic_cast usage, but the dummy method still doesn't seem right: now the base class holds the information (save() method) totally unrelated to it.



Method 3: Apply design patterns





  • Strategy pattern: SaveStrategy class and its subclasses like NoSaveStrategy, SomeSaveStrategy, SomeOtherSaveStrategy, etc. Again, the presence of NoSaveStrategy brings us back to the flaw of the previous method: base class has to know the particular details about its subclass, which seems like a bad design.


  • Proxy or Decorator patterns can easily encapsulate the dynamic_cast, however this will only hide the unwanted code, not get rid of the bad design itself.

  • Add some composition-over-inheritance layers, and so on and so on...


Question



Maybe I'm missing some obvious solution, or maybe the described methods (1 or 2) are not as bad and smelly in this particular context as I'm seeing them.



So what design approach is suitable in such situation?










share|improve this question

























  • you can still implement a pure virtual function. this would make it more or less a default behaviour. i am not sure about the smellyness though :-/

    – MauriceRandomNumber
    Nov 22 '18 at 15:05











  • Look at your problem under this angle: base class interface does not support foo. And you want to do via said interface (accessing real objects via base class pointers) something ( foo ) that it does not support.

    – Andrew Kashpur
    Nov 22 '18 at 15:06














3












3








3








Problem



I ran into a simple issue, though I can't come up with a proper OOD for it.



What I have:




  • Base class

  • Subclass adding a new method foo()

  • List of pointers to the base class instances


What I need:



I need to loop through this list and call the foo() for objects supporting this method, i.e. objects of (or derived from) the aforementioned subclass. Or speaking generally, I need a "non-smelly" polymorphic access to a subclass via list of pointers to a base class.



Example Code



class Entity {
// ...
// This class contains methods also needed by subclasses.
};

class SaveableEntity : public Entity {
public:
virtual void save() = 0;
};

// SaveableEntity has multiple subclasses with specific save() implementations.

std::vector<Entity *> list;
for (Entity *entity : list) {
// Here I need to save() the descendants of a SaveableEntity type.
}


I came up with some ideas, however none of them seem right to me. Here are some of them:



Method 1: dynamic_cast



As some elements are saveable and some are not, the most obvious way I see is dynamic casting:



std::vector<Entity *> list;
for (Entity *entity : list) {
auto saveable = dynamic_cast<SaveableEntity *>(entity);
if (saveable) {
saveable->save();
}
}


However, using dynamic_cast looks like a bad OOD in this situation (correct me if I'm wrong). Also, this approach can easily lead to the violation of the LSP.



Method 2: Move save() to a base class



I could remove SaveableEntity and move the save() method to the base Entity. However, this makes us implement dummy method:



class Entity {
virtual void save() {
// Do nothing, override in subclasses
}
};


This eliminates the dynamic_cast usage, but the dummy method still doesn't seem right: now the base class holds the information (save() method) totally unrelated to it.



Method 3: Apply design patterns





  • Strategy pattern: SaveStrategy class and its subclasses like NoSaveStrategy, SomeSaveStrategy, SomeOtherSaveStrategy, etc. Again, the presence of NoSaveStrategy brings us back to the flaw of the previous method: base class has to know the particular details about its subclass, which seems like a bad design.


  • Proxy or Decorator patterns can easily encapsulate the dynamic_cast, however this will only hide the unwanted code, not get rid of the bad design itself.

  • Add some composition-over-inheritance layers, and so on and so on...


Question



Maybe I'm missing some obvious solution, or maybe the described methods (1 or 2) are not as bad and smelly in this particular context as I'm seeing them.



So what design approach is suitable in such situation?










share|improve this question
















Problem



I ran into a simple issue, though I can't come up with a proper OOD for it.



What I have:




  • Base class

  • Subclass adding a new method foo()

  • List of pointers to the base class instances


What I need:



I need to loop through this list and call the foo() for objects supporting this method, i.e. objects of (or derived from) the aforementioned subclass. Or speaking generally, I need a "non-smelly" polymorphic access to a subclass via list of pointers to a base class.



Example Code



class Entity {
// ...
// This class contains methods also needed by subclasses.
};

class SaveableEntity : public Entity {
public:
virtual void save() = 0;
};

// SaveableEntity has multiple subclasses with specific save() implementations.

std::vector<Entity *> list;
for (Entity *entity : list) {
// Here I need to save() the descendants of a SaveableEntity type.
}


I came up with some ideas, however none of them seem right to me. Here are some of them:



Method 1: dynamic_cast



As some elements are saveable and some are not, the most obvious way I see is dynamic casting:



std::vector<Entity *> list;
for (Entity *entity : list) {
auto saveable = dynamic_cast<SaveableEntity *>(entity);
if (saveable) {
saveable->save();
}
}


However, using dynamic_cast looks like a bad OOD in this situation (correct me if I'm wrong). Also, this approach can easily lead to the violation of the LSP.



Method 2: Move save() to a base class



I could remove SaveableEntity and move the save() method to the base Entity. However, this makes us implement dummy method:



class Entity {
virtual void save() {
// Do nothing, override in subclasses
}
};


This eliminates the dynamic_cast usage, but the dummy method still doesn't seem right: now the base class holds the information (save() method) totally unrelated to it.



Method 3: Apply design patterns





  • Strategy pattern: SaveStrategy class and its subclasses like NoSaveStrategy, SomeSaveStrategy, SomeOtherSaveStrategy, etc. Again, the presence of NoSaveStrategy brings us back to the flaw of the previous method: base class has to know the particular details about its subclass, which seems like a bad design.


  • Proxy or Decorator patterns can easily encapsulate the dynamic_cast, however this will only hide the unwanted code, not get rid of the bad design itself.

  • Add some composition-over-inheritance layers, and so on and so on...


Question



Maybe I'm missing some obvious solution, or maybe the described methods (1 or 2) are not as bad and smelly in this particular context as I'm seeing them.



So what design approach is suitable in such situation?







c++ oop inheritance design-patterns polymorphism






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 21 at 18:06







kefir500

















asked Nov 22 '18 at 14:49









kefir500kefir500

2,71542633




2,71542633













  • you can still implement a pure virtual function. this would make it more or less a default behaviour. i am not sure about the smellyness though :-/

    – MauriceRandomNumber
    Nov 22 '18 at 15:05











  • Look at your problem under this angle: base class interface does not support foo. And you want to do via said interface (accessing real objects via base class pointers) something ( foo ) that it does not support.

    – Andrew Kashpur
    Nov 22 '18 at 15:06



















  • you can still implement a pure virtual function. this would make it more or less a default behaviour. i am not sure about the smellyness though :-/

    – MauriceRandomNumber
    Nov 22 '18 at 15:05











  • Look at your problem under this angle: base class interface does not support foo. And you want to do via said interface (accessing real objects via base class pointers) something ( foo ) that it does not support.

    – Andrew Kashpur
    Nov 22 '18 at 15:06

















you can still implement a pure virtual function. this would make it more or less a default behaviour. i am not sure about the smellyness though :-/

– MauriceRandomNumber
Nov 22 '18 at 15:05





you can still implement a pure virtual function. this would make it more or less a default behaviour. i am not sure about the smellyness though :-/

– MauriceRandomNumber
Nov 22 '18 at 15:05













Look at your problem under this angle: base class interface does not support foo. And you want to do via said interface (accessing real objects via base class pointers) something ( foo ) that it does not support.

– Andrew Kashpur
Nov 22 '18 at 15:06





Look at your problem under this angle: base class interface does not support foo. And you want to do via said interface (accessing real objects via base class pointers) something ( foo ) that it does not support.

– Andrew Kashpur
Nov 22 '18 at 15:06












2 Answers
2






active

oldest

votes


















2














There is solution #4 encouraged by data-oriented programming (there's been an excellent talk on it in the cppcon 2018, available on youtube): having two lists. One list is for all the SavableEntitys and the other for Entitys that are not savable.



Now, you iterate over the first list and ->save() those items.



The major advantage is that you only iterate over relevant entities. With some (probably major) refactoring, you could have a collection of objects rather than pointers to some. This would increase data locality and drastically decrease the number of cache misses.






share|improve this answer


























  • Interesting solution, thank you for the answer, +1

    – kefir500
    Nov 22 '18 at 15:12



















0














My first idea would have been to augment the base class with a virtual bool trySave() method. The default can be return false;, while SaveableEntity provides this override:



class SaveableEntity : public Entity {
public:
virtual bool trySave() final override
{
save();
return true;
}

// You could also make this protected.
virtual void save() = 0;
};


Whether this is more appropriate than @YSC's suggestion for your particular case is something that you have to decide for yourself. It's similar to moving save() into the base class but is significantly less confusing for the user.






share|improve this answer



















  • 1





    Thank you for the answer, However, although it improves the method #2, it still doesn't solve the "all-knowing" base class which (ideally) should know nothing about the derived classes.

    – kefir500
    Nov 26 '18 at 6:37













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53433458%2fcall-derived-class-method-from-base-pointer-list-loop-ood%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









2














There is solution #4 encouraged by data-oriented programming (there's been an excellent talk on it in the cppcon 2018, available on youtube): having two lists. One list is for all the SavableEntitys and the other for Entitys that are not savable.



Now, you iterate over the first list and ->save() those items.



The major advantage is that you only iterate over relevant entities. With some (probably major) refactoring, you could have a collection of objects rather than pointers to some. This would increase data locality and drastically decrease the number of cache misses.






share|improve this answer


























  • Interesting solution, thank you for the answer, +1

    – kefir500
    Nov 22 '18 at 15:12
















2














There is solution #4 encouraged by data-oriented programming (there's been an excellent talk on it in the cppcon 2018, available on youtube): having two lists. One list is for all the SavableEntitys and the other for Entitys that are not savable.



Now, you iterate over the first list and ->save() those items.



The major advantage is that you only iterate over relevant entities. With some (probably major) refactoring, you could have a collection of objects rather than pointers to some. This would increase data locality and drastically decrease the number of cache misses.






share|improve this answer


























  • Interesting solution, thank you for the answer, +1

    – kefir500
    Nov 22 '18 at 15:12














2












2








2







There is solution #4 encouraged by data-oriented programming (there's been an excellent talk on it in the cppcon 2018, available on youtube): having two lists. One list is for all the SavableEntitys and the other for Entitys that are not savable.



Now, you iterate over the first list and ->save() those items.



The major advantage is that you only iterate over relevant entities. With some (probably major) refactoring, you could have a collection of objects rather than pointers to some. This would increase data locality and drastically decrease the number of cache misses.






share|improve this answer















There is solution #4 encouraged by data-oriented programming (there's been an excellent talk on it in the cppcon 2018, available on youtube): having two lists. One list is for all the SavableEntitys and the other for Entitys that are not savable.



Now, you iterate over the first list and ->save() those items.



The major advantage is that you only iterate over relevant entities. With some (probably major) refactoring, you could have a collection of objects rather than pointers to some. This would increase data locality and drastically decrease the number of cache misses.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 22 '18 at 15:12

























answered Nov 22 '18 at 15:01









YSCYSC

22.4k351104




22.4k351104













  • Interesting solution, thank you for the answer, +1

    – kefir500
    Nov 22 '18 at 15:12



















  • Interesting solution, thank you for the answer, +1

    – kefir500
    Nov 22 '18 at 15:12

















Interesting solution, thank you for the answer, +1

– kefir500
Nov 22 '18 at 15:12





Interesting solution, thank you for the answer, +1

– kefir500
Nov 22 '18 at 15:12













0














My first idea would have been to augment the base class with a virtual bool trySave() method. The default can be return false;, while SaveableEntity provides this override:



class SaveableEntity : public Entity {
public:
virtual bool trySave() final override
{
save();
return true;
}

// You could also make this protected.
virtual void save() = 0;
};


Whether this is more appropriate than @YSC's suggestion for your particular case is something that you have to decide for yourself. It's similar to moving save() into the base class but is significantly less confusing for the user.






share|improve this answer



















  • 1





    Thank you for the answer, However, although it improves the method #2, it still doesn't solve the "all-knowing" base class which (ideally) should know nothing about the derived classes.

    – kefir500
    Nov 26 '18 at 6:37


















0














My first idea would have been to augment the base class with a virtual bool trySave() method. The default can be return false;, while SaveableEntity provides this override:



class SaveableEntity : public Entity {
public:
virtual bool trySave() final override
{
save();
return true;
}

// You could also make this protected.
virtual void save() = 0;
};


Whether this is more appropriate than @YSC's suggestion for your particular case is something that you have to decide for yourself. It's similar to moving save() into the base class but is significantly less confusing for the user.






share|improve this answer



















  • 1





    Thank you for the answer, However, although it improves the method #2, it still doesn't solve the "all-knowing" base class which (ideally) should know nothing about the derived classes.

    – kefir500
    Nov 26 '18 at 6:37
















0












0








0







My first idea would have been to augment the base class with a virtual bool trySave() method. The default can be return false;, while SaveableEntity provides this override:



class SaveableEntity : public Entity {
public:
virtual bool trySave() final override
{
save();
return true;
}

// You could also make this protected.
virtual void save() = 0;
};


Whether this is more appropriate than @YSC's suggestion for your particular case is something that you have to decide for yourself. It's similar to moving save() into the base class but is significantly less confusing for the user.






share|improve this answer













My first idea would have been to augment the base class with a virtual bool trySave() method. The default can be return false;, while SaveableEntity provides this override:



class SaveableEntity : public Entity {
public:
virtual bool trySave() final override
{
save();
return true;
}

// You could also make this protected.
virtual void save() = 0;
};


Whether this is more appropriate than @YSC's suggestion for your particular case is something that you have to decide for yourself. It's similar to moving save() into the base class but is significantly less confusing for the user.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 22 '18 at 15:21









Max LanghofMax Langhof

10k1838




10k1838








  • 1





    Thank you for the answer, However, although it improves the method #2, it still doesn't solve the "all-knowing" base class which (ideally) should know nothing about the derived classes.

    – kefir500
    Nov 26 '18 at 6:37
















  • 1





    Thank you for the answer, However, although it improves the method #2, it still doesn't solve the "all-knowing" base class which (ideally) should know nothing about the derived classes.

    – kefir500
    Nov 26 '18 at 6:37










1




1





Thank you for the answer, However, although it improves the method #2, it still doesn't solve the "all-knowing" base class which (ideally) should know nothing about the derived classes.

– kefir500
Nov 26 '18 at 6:37







Thank you for the answer, However, although it improves the method #2, it still doesn't solve the "all-knowing" base class which (ideally) should know nothing about the derived classes.

– kefir500
Nov 26 '18 at 6:37




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53433458%2fcall-derived-class-method-from-base-pointer-list-loop-ood%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Create new schema in PostgreSQL using DBeaver

Deepest pit of an array with Javascript: test on Codility

Costa Masnaga