CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 5 1234 ... LastLast
Results 1 to 15 of 64

Thread: Abstraction concept problem?

  1. #1
    Join Date
    Dec 2010
    Posts
    907

    Abstraction concept problem?

    When I design my application, I always want to group up everything in common
    to the base class, and have other classes which have that component
    to get a pointer which is the one that is as abstract as possible.
    But in some of my applications, I use boost::shared_ptr a lot,
    When I put the component as abstract as possible, when it goes to
    implementation, I always want to do a boost:ynamic_point_cast
    to the concrete class, which is a downcast.
    Is it recommended? or is it a good coding practice?
    I sometimes, or maybe confused, that upcast is the common practice where downcast is not recommended?
    I find some of my pointers are not allowed to be converted by the compiler?
    Thanks
    Jack

  2. #2
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Abstraction concept problem?

    if you are finding yourself to be using a lot of pointers in designing interfaces... you're probably doing something wrong somewhere.

    If abstraction is causing you to introduce pointers, you should be looking at inheritance instead.

  3. #3
    Join Date
    Mar 2001
    Posts
    2,527

    Re: Abstraction concept problem?

    I met a man who said pointers were evil and did everything he could to avoid them. I don't go that far, but I think you should only use a pointer where it is really necessary.

    It is cumbersome that even with "built-in" polymorphism one should have to use a cast at all.
    ahoodin
    To keep the plot moving, that's why.

  4. #4
    Join Date
    Jun 2015
    Posts
    208

    Re: Abstraction concept problem?

    Quote Originally Posted by lucky6969b View Post
    upcast is the common practice where downcast is not recommended?
    One reason is type safety. An upcast can be fully checked at compiletime and so will never fail a runtime. That's why you don't have to cast explictly in that case if you don't want to. A downcast on the other hand may fail at runtime with disastrous results. That's why the compiler forces you to take on full responsibility for any dire consequences by insisting on an explicit cast so you cannot claim you weren't aware it could turn bad.

    Another reason is good design. Downcasting is the very opposite of abstraction. You first spend a lot of time writing general easy to extend code using abstract classes. Then you ruin the whole effort by tying down your code to concrete specfic classes by downcasting. Very counter-productive.

    How to avoid downcasting is a long story but here are a few basic steps you can take,

    1. Make each base class represent one single well defined concept. Then they will cover well for their derived classes.

    1. Make sure the base class is in a proper is-a relationship with all anticipated derived classes. Apple is-a Fruit works but ApplePie is-a Fruit doesn't.

    2. Prefer a base class to do things rather than being just a storage for setting and getting information. Make them agents of action rather than mere records for data shuffling.

    I find some of my pointers are not allowed to be converted by the compiler?
    That's what happens when you break the rules of the language so don't blame the compiler.
    Last edited by tiliavirga; July 1st, 2015 at 03:24 AM.

  5. #5
    Join Date
    Jun 2015
    Posts
    208

    Re: Abstraction concept problem?

    Quote Originally Posted by ahoodin View Post
    I met a man who said pointers were evil and did everything he could to avoid them.
    Did he explain why?

    What about polymorphic designs (like in this thread)?

    What about the slicing problem?
    Last edited by tiliavirga; July 1st, 2015 at 02:43 AM.

  6. #6
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Abstraction concept problem?

    pointers are evil
    and you should avoid them if possible

    that doesn't mean they're Always avoidable and aren't sometimes necessary.
    But as a whole, you tend to have cleaner and easier to use interfaces if you do avoid them.

  7. #7
    Join Date
    Jun 2015
    Posts
    208

    Re: Abstraction concept problem?

    Quote Originally Posted by OReubens View Post
    pointers are evil
    and you should avoid them if possible
    Congratulations!!! You've just won the Dogmatic Statement of the Month competition.

    I'll continue using every C++ feature for best effect and certainly pointers in OO paradigmic code where they are an indispensable tool (in the form of smart pointers of course) for high quality designs.

  8. #8
    Join Date
    Mar 2001
    Posts
    2,527

    Re: Abstraction concept problem?

    Quote Originally Posted by tiliavirga View Post
    Did he explain why?
    Yep. He is religious and said they were a tool of the devil.
    He also went on to complain about a lot of misuse and abuse
    leading to strange crashes after code with many pointers
    was edited by other software engineers.

    Quote Originally Posted by tiliavirga View Post
    What about polymorphic designs (like in this thread)?
    Uh yeah that was my point, so I will claim that one.
    Quote Originally Posted by tiliavirga View Post
    What about the slicing problem?
    same thing.

    Pointers are among the most misused and abused constructs
    in C and C++. You may not be among the list of abusers, but
    consider the context of a large project where you have a range
    of skills working on it. Say 50% don't really have a good knowledge
    of pointers but try to do it anyway. What do you get?

    Memory Exceptions, Segmentation Faults, funky behavior, problems, trouble.
    Last edited by ahoodin; July 1st, 2015 at 11:28 AM. Reason: Memory Exceptions added to list
    ahoodin
    To keep the plot moving, that's why.

  9. #9
    Join Date
    Jan 2010
    Posts
    1,133

    Re: Abstraction concept problem?

    Pointers points aside (heh! ), a few more things about the design question (downcasting in client code):
    While type-casting from abstractions to concrete types in client code is not necessarily bad (especially if you are certain what the exact type is going to be), and while it's sometimes unavoidable, in general it indicates that there's something not quite right in your design, and it should be avoided - and what that really means that you should spend some time figuring out if and how you should redesign you classes.

    Ideally, the client code (the code that uses your components) should be able to achieve what it wants by only using the abstract interface. The concrete implementations can then vary the details of the behavior, but client code should not be aware of them (although, sometimes it's not that easy, because abstractions are often "leaky").
    If that's not possible, then either the abstraction does not adequately represent the component, or the client code doesn't actually operate on an abstraction, but rather on concrete types, and could be possibly broken into several distinct functions that represent different tasks on concrete types. Some of that functionality may not even belong in the client code - it could be that its better moved to the concrete classes themselves, or maybe to some other class entirely.

    All this requires you to think about what your system actually needs to do, and how you can represent different things.
    For example, if you have a class called Animal, with a virtual function called Run(), you can create a bunch of different, specific animals (subtypes), pass them all to the client code as Animal-s, and then call Run() on each to have different subclasses take care of concrete implementation details for each subtype.

    But what if one of the derived types is Bird, and what if Run() doesn't make sense for that class; rather, it should do something like Fly(). There are several things you could do, depending on the system you are designing, and also, depending on wether it makes sense within your conceptual model.

    Yes, you could check the actual type and down-cast if it turns out it is a Bird. But, if you think it through, and maybe think a bit out of the box, you may realize (for example) that your client code doesn't really care about wether the animal runs or flies - rather, the core logic behind it might be something like "move fast", or "escape". So, you rename the Run() function to something like MoveFast(), or Escape(), or whatever, and then suddenly, because of this shift in semantics, you can implement the fly behavior in this method. You've designed a better abstraction.

    Sometimes, the class that contains the client code tries to do something that really isn't it's job, and so it ends up downcasting in order to extract some type specific data, or call type specific methods. Such client code is violating what's called the "tell, don't ask" principle. Basically, it asks for a bunch of type-specific things so that it could do something on it's own, instead of just telling the other class (that already has access to all this information) to do for it. Of course, for this to be possible, the interface of the other class should expose such a function. Often, the best place to put a specific functionality is the class that has all, or most, of the information and/or capabilities required to implement that functionality (this class may take some of that information from other objects it contains or references, but it would do so through their corresponding interfaces, without any typecasting).

    At other times, there might not be a way to meaningfully capture and abstract away all the specific behaviors - if you create a base class in such a situation anyway, you'll end up downcasting a lot, since functions that operate on such objects are really designed to work with concrete types. Depending on the situation, it might be OK/safe to downcast, or it might be better to redesign the system.

    Another thing that may lead to type-casting could be the need to implement an operation so that it doesn't just depend on the concrete subclass of a single object, but also on the concrete subclass of another object - this is know as double dispatch. That is, the concrete function implementation that ends up being called depends on the actual types of two objects. In languages that do not support double-dispatch, the problem is solved by applying the Visitor pattern. However, its best to try and understand the system better and look for alternatives before applying this design pattern, because it's somewhat convoluted, and sometimes it's possible to use downcasting just as effectively, and avoid elaborate, convoluted designs (and it may also perform better).

    Another solution, that's usually not a good choice, but can sometimes make sense, is to conceptualize of some of the concrete classes as types for which a certain function (that makes sense for other types in the hierarchy) is simply represented by a no-op (a function that does nothing). This then allows you to pull out that function into the base class, and treat all the instances of the derived types in the same way.
    Sometimes, simply doing nothing is a sensible thing to do. Even if that's not the "happiest" choice, sometimes it may be easier to do it that way, without introducing too much confusion. For example, in a game, you could have objects that can take Damage(), and some objects that would be undamageable/indestructible. It may make (enough) sense, or it may just be easier, to implement Damage() as a no-op for such objects. Or maybe you have a graph that you need to draw, say a tree which contains a bunch of instances of Node-derived types that implement the Draw() method; say also that you have a special subtype of Node that, among other things, hides the entire subtree rooted at it. It's not unreasonable to have this node implement Draw() as a no-op; it just requires a slight shift in the way you think about what Draw() actually means. These examples are somewhat contrived, I know, but you get the point.
    Don't make a habit of it, though, as it can lead to other design problems if it turns out it's not the right way to go. E.g., using the animal scenario above, you could have implemented Bird::Run() as a no-op, and then changed the base class to contain both Run() and Fly(), but then you'd be just polluting the base class, you'd have to implement Fly() as no-op for all the non-flying animals, and it would generally be a mess.

    An idea similar to this is to conceptually widen the meaning of a certain operation, so that it makes sense to implement behaviors that made less sense when the meaning was narrower - but then it might be better to rename the function. Be careful not to have implementations that are largely unrelated to what the function is supposed to represent, because that will make the code confusing, and hard to use.

    Downcasting is not all bad, though. Say you're working within some framework that provides an event system, and event handlers all receive some data about the event through an abstraction called EventData. If you're the one who hooked up these event handlers, then you'll know which concrete event data subtype was used when the event was raised, so, in the handler, it's OK to downcast, because it doesn't really affect anything, so, who cares. Do what's right for the situation. When it comes to design, have the scope and the purpose of the system in mind - don't overdesign due to looking too far ahead and worrying about hypothetical future changes and scenarios that might never happen. Sure, if something needs to be supported in the future, you might need to do some refactoring, but that's OK because you'll understand the new requirements better at that time; refactoring is also much less painful then trying to forcefully make something work with a system that has been designed in a wrong way from the beginning.
    Last edited by TheGreatCthulhu; July 1st, 2015 at 05:21 PM.

  10. #10
    Join Date
    Jun 2015
    Posts
    208

    Re: Abstraction concept problem?

    Quote Originally Posted by ahoodin View Post
    Pointers are among the most misused and abused constructs
    in C and C++. You may not be among the list of abusers, but
    consider the context of a large project where you have a range
    of skills working on it. Say 50% don't really have a good knowledge
    of pointers but try to do it anyway. What do you get?

    Memory Exceptions, Segmentation Faults, funky behavior, problems, trouble.
    I agree. You are using the same argument James Gosling used some 20 years ago when he declared C++ unsafe and introduced Java (which later spawned C#). But remember he did never discard pointers! They're live and well in both Java and C# only not in raw form but dressed up in a nice clothing.

    So should C++ programmers refrain from pointers altogether while their Java and C# colleagues get to enjoy them everyday? I don't think so but I recognice that C++ is different and requires a different approach. C++ is a versatile systems programming language which allows you to do things a Java or C# programmer can only dream about. That's both a curse and a blessing and the trick to deal with this double-edged sword is to apply self-control. C++ programmers must learn to restrict themselves to safe and sound practices. If they cannot do that they're better off using Java or C# or some other language where good behaviour is imposed upon them rather than a matter of choise.

    Fortunately in recent years C++ has evolved to the point (read versons 11 and 14) where it is as safe as Java and C#. And safe pointer usage is straightforward: Prefer smart pointers over raw pointers, prefer iterators over pointer arithmetics and prefer implicit iteration constructs over iterators. Simple as that.

    Pointers are an essential part of C++. Banning them is not an option. Instead use them safely by graduating to modern C++. If you are unable to make that leap consider Java or C#. C++ is not for everyone.

    same thing.
    You are either underestimating or not aware of the slicing problem in C++. Slicing is a consequence of not holding polymorphic objects by pointer. If you don't you will sooner or later copy such objects by value which will cut them to pieces.

    in other words, by banning pointers because they're unsafe you are opening the door for another equally severe or even worse safety issue - object slicing. So be smart and hold polymorphic objects by smart pointer.
    Last edited by tiliavirga; July 2nd, 2015 at 03:15 AM.

  11. #11
    Join Date
    Oct 2008
    Posts
    1,456

    Re: Abstraction concept problem?

    Quote Originally Posted by tiliavirga View Post
    Pointers are an essential part of C++. Banning them is not an option. Instead use them safely by graduating to modern C++. If you are unable to make that leap consider Java or C#. C++ is not for everyone.
    historically, pointers mostly meant "mutable references"; nowadays, (smart)pointers should strictly means "ownership". An interface that use smart pointers to pass ownership of dependencies around is ok of course and fundamental for any c++ OOP design;
    an interface that uses pointers (smart or dumb) where simple composition/references/etc... can be used are very bad IMO.

    Quote Originally Posted by tiliavirga View Post
    You are either underestimating or not aware of the slicing problem in C++. Slicing is a consequence of not holding polymorphic objects by pointer. If you don't you will sooner or later copy such objects by value which will cut them to pieces.
    aren't you overestimating it ? slicing can be prevented by properly defning copy/assign/move ctor/operators, something that people often do anyway with polymorphic types, and it's very easy and expressive nowadays ( c++11's "= delete/default" ) .

  12. #12
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Abstraction concept problem?

    Quote Originally Posted by tiliavirga View Post
    Congratulations!!! You've just won the Dogmatic Statement of the Month competition.
    it's not a dogma. it's guideline. That's a pretty different take on it.

    If you are working on projects that are long lived, and need to be maintained by many people, then the guideline is a good guideline to follow.

    Note that I also stated pointers in public interfaces are evil. It's hard to avoid all occurrences of them, so in private interfaces you will see them, as well as in the implementations of (member)functions.

  13. #13
    Join Date
    Jun 2015
    Posts
    208

    Re: Abstraction concept problem?

    Quote Originally Posted by OReubens View Post
    it's not a dogma. it's guideline.
    A guideline that is neither motivated nor qualified is a dogma.

    Besides, your guideline is out of touch with the consensus view. Avoiding or banning pointers was never an option. Neither Gosling of Java nor Hejlsberg of C# did away with pointers when they had the chance. Instead they domesticated them.

    Now Stroustrup of C++ also has tamed the pointers but with a twist. In true C++ spirit there is a choise. You can be nice and gentle for safety but also play it rougth where it counts. In advice form: Prefer pointer abstractions (like smart pointers and iterators) over raw pointers.
    Last edited by tiliavirga; July 3rd, 2015 at 01:24 AM.

  14. #14
    Join Date
    Jun 2015
    Posts
    208

    Re: Abstraction concept problem?

    Quote Originally Posted by superbonzo View Post
    historically, pointers mostly meant "mutable references";
    Pointer historically means hardware address register. That's how pointers were viewed in C when it was introduced as a high-level assembly language in 1972, and that's how some C++ programmers still view pointers today, and that's a problem. Just listen to Stroustrup in The C++ Programming Language, fourth edition, page 19:

    "Often, what an experienced C++ programmer has failed to notice over the years is not the introduction of new features as such, but rather the changes in relationship between features that make fundamental new programming techniques feasible".

    In other words: Don't be stuck in old thinking. Go for modern C++ 11.

    aren't you overestimating it ?
    Not at all. The number of C++ programmers who have never heard about the slicing problem are many, far too many.

    But certainly, if you know about slicing then take proper steps to prevent it. In advice form: If an object should not be copyable then disable copying at the language level (deleting the copy constructor and copy assignment operator as you mention). If copies still are wanted supply a clone method. Especially to the OP who seems to be using Boost already: Consider boost::noncopyable for the polymorphic base classes.
    Last edited by tiliavirga; July 6th, 2015 at 09:23 AM.

  15. #15
    Join Date
    Dec 2010
    Posts
    907

    Re: Abstraction concept problem?

    If pointers are not recommended in most cases, how should I deal with the following situations?
    1) when I need to pass another object at the constructor?
    2) when I need to change the pointed object within another object?
    3) when I want to avoid keeping a lot of instances of the same object by value?
    In these cases, I do really need pointers
    Thanks
    Jack

Page 1 of 5 1234 ... LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width




On-Demand Webinars (sponsored)