• 516052 (unregistered)

    "combines the syntactic elegance of C with the run-time efficiency of Smalltalk"

    God. This had me laughing.

  • (nodebb)

    The type check seems to be unnecessary anyway. GetFirstTentacle is clearly allowed to return null, even if called against an octopus or squid (presumably some of them have lost all their tentacles). So there should be no issue skipping the type check and calling GetFirstTentacle anyway. If it's a cat, it should just return null - we don't care what it is, just that it has no tentacles to steal. Which also means that if someone adds a tentacled cat to the system it won't break this function. .

  • (nodebb)

    // only Octopus and Squid can have tentacles

    Sadly incorrect. An octopus has arms rather than tentacles, and there are plenty of species that aren't octopuses or squids that have tentacles of some sort, including one species of mammal, the star-nosed mole.

    Their comment highlights their misunderstanding of the function; dynamic_cast can be used to crosscast. In other words, dynamic_cast<Dog*>(my_cat) will actually work, because (in this example) Dog* and Cat* share a common base.

    It will, indeed, work, in that it will do what it is supposed to, and cross-casting like that is not part of what it is supposed to do. The return value will be NULL or, more modernly, nullptr, if you try to cast between two classes that are not part of the same inheritance tree. (If there's a class DogCat which derives from Cat and from Dog, and you have a pointer to the Cat part of a DogCat, then you can dynamic_cast from Cat * to Dog * and you'll get the Dog part of the same object that the Cat part is part of. But in general, you can't do an arbitrary crosscast.

    This code attempts to take an objectPointer and first, force it into a pointer to our base type (Organism), using a C-style cast.

    Bad programmer! There are legitimate uses for C-style casts in C++, but a certain amount of care is needed, and they shouldn't be used as a substitute for the correct C++-style cast (in this case, it's not necessary anyway, since dynamic_cast will search through the inheritance tree of the object from any starting point). (There is only one thing that you can do only with a C-style cast, namely (static) casting to a private base class that's actually a base of the requested object, which is something you shouldn't do anyway.)

    a const_cast (which only alters the constness of the type)

    More correctly, const_cast removes "cv qualifiers", i.e. const and/or volatile.

  • (nodebb) in reply to SteelCamel2

    Also curious as to how it treat sentient tentacles that come in green and purple hues.

  • (nodebb)

    I think some people missed the point. Instead of having an override function that says "you're an animal, do you support tentacles, yes/no?" the base class is checking what derived class was passed in to decide. The code is anonymized so it could have been something else not tentacle-related like "can you fly?", "do you breathe oxygen?", "can you sting people?", etc....

  • (nodebb) in reply to SteelCamel2

    They're already applying that optimization to the animal you're stealing a tentacle from. They "have" to do it here to enforce the same constraint on the animal you're stealing the tentacle to.

  • (nodebb) in reply to davethepirate

    Yes. Complaining about the type of cast or how the class of a parameter is being determined is a bit like complaining about the colour of the iceberg that hit the Titanic.

    There should be a definition of getFirstTentacle() in the base class that always returns null and it should be overridden in classes that have tentacles.

    However

    It does look like any Animal can attach a tentacle to itself, even if it wasn't born with any, so perhaps the implementation should include a tentacle collection in the base class which starts empty for most Animals and is populated with 8 or 10 tentacles in the constructors for Octopus and Squid respectively. The functions can then be fully defined in Animal

  • Argle (unregistered)

    Dang! My octopus lost an arm! I'm checking the manual, but I can't find a number for tentacle support.

  • (nodebb)

    To Remi's opening point: Back in the '90s, when I was first introduced to OOP, tutorials indeed liked to use animals, as well as vehicles. (I especially recall bicycles, but cars were in there, too.) None of it made any sense whatsoever, until I found something that explained object-oriented programming in terms of programming. It made perfect sense then. Gosh. Imagine that.

  • (nodebb)

    Has nobody noticed that the code is checking if this is an Octopus or Squid, and not animal? An octopus can steal a tentacle from a cat, but a cat can't steal a tentacle from an octopus.

  • (nodebb)

    The other common examples I remember were shapes: Square IS-A Rectangle IS-A Quadrilateral. They would relate it to programming a graphics drawing or rendering application.

    Vehicles were often used to show the distinction between IS-A and HAS-A. Automobile HAS-AN Engine, Sedan IS-AN Automobile.

  • airdrik (unregistered) in reply to Barry Margolin

    And shapes is one of the first places where you'll get in trouble with the analogy. A square is-a rectangle where all of the sides are the same. and/but A rectangle is-a square that also has a separate length property. So which one is it? Neither one provides a fully satisfactory answer, depending on the situation and what you're trying to do with them. (though more likely you'd go with they're all Shapes (that are quadrilaterals), some of which happen to satisfy certain criteria).

  • Dwaz (unregistered) in reply to airdrik

    If square derives from rectangle, then a rectangle with equal length and width isn't a square according to the type system, which is a bit unfortunate. But anyone who models rectangle to derive from square because they just both happen to share a width property is truly insane.

  • Dwaz (unregistered) in reply to Dwaz

    To be clear, I agree that the shapes analogy is quite poor and falls apart almost immediately, but having some type derive from some other type because they happen to share some arbitrary set of properties or functionality is always a recipe for bad code. In the square/rectangle example, assuming inheritance should be used at all (or that square should even be a separate type), the only sensible answer is that square derives from rectangle, simply because all squares are rectangles, and not all rectangles are squares.

  • Randal L. Schwartz (github) in reply to Steve_The_Cynic

    Sadly incorrect. An octopus has arms rather than tentacles, and there are plenty of species that aren't octopuses or squids that have tentacles of some sort, including one species of mammal, the star-nosed mole.

    How DARE YOU bring actual SCIENCE to a discussion!!!

  • (nodebb)

    I think this article misses the point a bit: It's not inheritance that confuses inexperienced developers, it's actually multi-inheritance. There is no single-is relationship, there can actually be multiple is-es with multiple sub-instances. Hence type-casting is way less straight forward and especially young devs with zero experience in multi-inheritance go sometimes a bit crazy with their limited understanding.

  • (nodebb) in reply to Dwaz

    If square derives from rectangle, then a rectangle with equal length and width isn't a square according to the type system, which is a bit unfortunate.

    It can work in the sense that a Rectangle type would have two separate size fields where as a Square type would have just one. It may all come down to what the needs are, though I do agree that it might not be the best example to learn about inheritance from.

  • (nodebb) in reply to Randal L. Schwartz

    How DARE YOU bring actual SCIENCE to a discussion!!!

    I'm like that. Not sorry.

  • (nodebb) in reply to airdrik

    And shapes is one of the first places where you'll get in trouble with the analogy. A square is-a rectangle where all of the sides are the same. and/but A rectangle is-a square that also has a separate length property. So which one is it? Neither one provides a fully satisfactory answer, depending on the situation and what you're trying to do with them. (though more likely you'd go with they're all Shapes (that are quadrilaterals), some of which happen to satisfy certain criteria).

    Pretty much this, although I prefer to phrase it in terms of the Liskov Substitution Principle, where we note that according to the LSP, (paraphrasing "slightly") if we give a Square that's derived from Rectangle to a bit of code that expects a Rectangle, it should not be able to tell that it isn't a base Rectangle. See for example this pseudocode:

    class Rectangle : public Shape
    {
        // blabla including a set_width() method and a set_height() method and a get_area() method that returns the area of the object.
    };
    
    class Square : public Rectangle
    {
        // blabla including overridden set_width() and set_height() that change both height and width to the given size, but without an overridden get_area().
    };
    
    Square square(initSquareSize);
    
    Rectangle &rect = square;
    
    rect.set_width(5);
    rect.set_height(4);
    
    assert(rect.get_area() == 20);
    

    LSP says that the assert() should be OK because we think we're dealing with a Rectangle. Reality says that it explodes because we're dealing with a Square.

    Conclusion, this version of "Square IS-A Rectangle" is bogus. (If they are immutable, like Java and C♭ strings, that might change, but in any case, the above situation invalidates the unqualified "a Square IS-A Rectangle" statement. It has to be qualified by "if they are immutable".)

  • (nodebb) in reply to Steve_The_Cynic

    An easy way around that problem is to have Square not use width and height but a separate field, like size, and its own get_area that multiplies size by 2. It might also be better to have Square derive from Shape instead of Rectangle or perhaps have Quadrilateral, that derives from Shape, and is the base for both Rectangle and Square (as well as Rhombus, Parallelogram, Trapezoid,Kite, etc.)

    Addendum 2026-10-14 08:42: Oops, that should be:

    its own get_area that multiplies size by itself.

  • Twither (unregistered) in reply to gordonfish

    You're missing the point. Square is a property of rectangles that satisfy a certain constraint. When we talk about classes, objects, and inheritance, we're not talking about mathematical properties, but data representations. So you might represent a Rectangle with a length and width, and a certain subset of those representations are square. A data representation of a Square will have a single size parameter; therefore, Square does not extend Rectangle, but constrains Rectangle.

    This is why Interfaces are preferred over inheritance. Square and Rectangle both satisfy the conditions for IRectangle (having Length and Width getters), allowing a Square to sit anywhere you need an IRectangle, but not when you need a Rectangle.

    Regardless, this is all a very bad idea, as abstract shapes have no idea how to render themselves. As soon as you want to, heaven forbid, draw a shape, you need to know its details, which means knowing every single shape that can exist. Similarly, if you want to do collision detection, or calculate shape intersections. As long as shapes are simple data representations, all of this is easy. Once you apply the principles of OOP and hide the implementation details, these real-world tasks become almost impossible.

Leave a comment on “A Type of Type Safety”

Log In or post as a guest

Replying to comment #705286:

« Return to Article