Call derived class method from base pointer list loop (OOD)
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 likeNoSaveStrategy
,SomeSaveStrategy
,SomeOtherSaveStrategy
, etc. Again, the presence ofNoSaveStrategy
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 thedynamic_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
add a comment |
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 likeNoSaveStrategy
,SomeSaveStrategy
,SomeOtherSaveStrategy
, etc. Again, the presence ofNoSaveStrategy
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 thedynamic_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
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
add a comment |
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 likeNoSaveStrategy
,SomeSaveStrategy
,SomeOtherSaveStrategy
, etc. Again, the presence ofNoSaveStrategy
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 thedynamic_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
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 likeNoSaveStrategy
,SomeSaveStrategy
,SomeOtherSaveStrategy
, etc. Again, the presence ofNoSaveStrategy
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 thedynamic_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
c++ oop inheritance design-patterns polymorphism
edited 12 hours ago
kefir500
asked Nov 22 '18 at 14:49
kefir500kefir500
2,70542333
2,70542333
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
add a comment |
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
add a comment |
2 Answers
2
active
oldest
votes
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 SavableEntity
s and the other for Entity
s 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.
Interesting solution, thank you for the answer, +1
– kefir500
Nov 22 '18 at 15:12
add a comment |
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.
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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 SavableEntity
s and the other for Entity
s 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.
Interesting solution, thank you for the answer, +1
– kefir500
Nov 22 '18 at 15:12
add a comment |
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 SavableEntity
s and the other for Entity
s 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.
Interesting solution, thank you for the answer, +1
– kefir500
Nov 22 '18 at 15:12
add a comment |
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 SavableEntity
s and the other for Entity
s 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.
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 SavableEntity
s and the other for Entity
s 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.
edited Nov 22 '18 at 15:12
answered Nov 22 '18 at 15:01
YSCYSC
21.3k348101
21.3k348101
Interesting solution, thank you for the answer, +1
– kefir500
Nov 22 '18 at 15:12
add a comment |
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
add a comment |
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.
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
add a comment |
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.
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
add a comment |
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.
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.
answered Nov 22 '18 at 15:21
Max LanghofMax Langhof
9,2751537
9,2751537
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
add a comment |
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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