1

I'm trying to extend someone else's API for my own purposes, and I'm trying to get my head around class inheritance in that context.

I've written this minimal program:


    #include 

    class first_class {
    public:
        first_class() {};
    };

    class second_class : first_class {
    private:
        int y;

    public:
        int get_y() { return y; }
        second_class() {
            y=99;
        }
    };

    int main() {
        first_class* a = new first_class();
        second_class* b = new second_class();

        int q = ((second_class*)a)->get_y();
        printf("%d\n",q);

        int r = b->get_y();
        printf("%d\n",r);
    }

first_class does nothing at all, and has no members. second_class inherits from first_class, but adds a private member y and a getter function, get_y();

When I call get_y() on an instance of second_class, it works as expected. When I call get_y() on instance of first_class, which I have cast to second_class, it returns 0.

Is that what should be expected? Are all child-class-only member variables automatically set to zero, or is that one of those usually-happens-but-not-actually-guaranteed things that compilers do sometimes, and they should actually be treated as undefined?

  • 2
    `((second_class*)a)->get_y();` invokes *undefined behavior* - anything is allowed to happen at this point – UnholySheep Apr 06 '19 at 22:23
  • 2
    Note that it is not possible to figure out whether program has undefined behavior by writing tests. That is it is wrong to have expectations for this program on the first place. – user7860670 Apr 06 '19 at 22:28
  • If you're learning C++, don't use old style cast, i.e. `(second_class*)`. Use `static_cast` instead, `dynamic_cast` (when using polymorphic classes) or `reinterpret_cast` (only when needed, **most** of the time, you're doing something wrong). Your old style cast is doing silently in this case a `reinterpret_cast` operation. – Mirko Apr 07 '19 at 00:56

2 Answers2

4

When you cast a pointer, it does not "convert" the pointed object (nor affects it in any way). It is a noop, i.e. nothing happens at run-time.

Instead, it simply changes what the type of the pointed object is thought to be. This is a compile-time concept.

Therefore, when you write:

(second_class*)a

You are saying: "I really know the memory pointed by a is a second_class object and I want to access it as such".

But it isn't, it is a first_class. So accessing the y data member does not make sense. The compiler will end up generating, most likely, code that ends up reading memory that you shouldn't.


Now, there is a way to call member functions through the base class, i.e. through a pointer or reference of a base class of the actual object. You can read about it (subtyping, polymorphism and dynamic dispatch are topics you may want to read about). But in C++ it requires:

  • That you are accessing through a base class (not a derived one like you are doing here); i.e. upcasting vs. downcasting.
  • That the class hierarchy is meant to be used that way. Typically methods and destructor will be virtual throughout the hierarchy (which you don't have in your example).

See, for instance: Why do we need virtual functions in C++? to start learning about it.

Acorn
  • 24,970
  • 5
  • 40
  • 69
  • 1
    "When you cast a pointer, (...) It is a noop". False in many contexts. It's untrue for multiple inheritance and for polymorphic classes. The fact is: `reinterpret_cast` is a no-op. `static_cast` usually could be a sum and `dynamic_cast` includes a lookup. C-style cast can do any of those depending on context. – Mirko Apr 07 '19 at 00:53
  • 1
    @mirko technically even a reinterpret cast can have runtime effects; for example, on hardware where null ptr is "all 1s" (some gpu hardare for example), I believe reinterpreting a 0 integral value as a pointer will return a null pointer. But that is getting esoteric. – Yakk - Adam Nevraumont Apr 07 '19 at 01:29
  • @Mirko First of all, if you read carefully, I was talking about the *pointed* object, not the pointer. You took two independent sentences and merged them wrongly. Second, C-style casts cannot trigger `dynamic_cast`s. As for `static_cast`, what sum are you referring to? Because I am afraid you are getting here into environment-dependent topics. Finally, you should note that OP does not have a hold on the basics. The fact is: simplifying goes a very long way for teaching. – Acorn Apr 07 '19 at 11:57
2

Is that what should be expected?

No, ((second_class*)a)->get_y(); invokes undefined behavior

Are all child-class-only member variables automatically set to zero, or is that one of those usually-happens-but-not-actually-guaranteed things that compilers do sometimes

Neither, you are effectively reading memory that doesn't belong to the object. Anything can happen (hence undefined behavior)

UnholySheep
  • 3,967
  • 4
  • 19
  • 24