1

Consider this:

#include <iostream>
using namespace std;
class A{
   protected:
       void some_function(int params)
       {
        //inside A: do something A related 
       }
};

class B: public A{
public:
    void call_some_function(int params)
    {
    some_function(params); // Simple call to A::some_function and NOTHING MORE.
    }
};


int main(int argc, char *argv[])
{
   A* a = new A();

   ((B*)(a))->call_some_function(20); // Is it OK ?
}

This code works What is the danger of using it ?

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
Orochi
  • 395
  • 4
  • 13
  • Declare `virtual void call_some_function(int params)` and your code will magically stop working. – Lyth Dec 08 '11 at 14:30
  • What if the point of declaring it is to access the protected or a private member of class A and make it available to program, Is it OK in such case ? – Orochi Dec 08 '11 at 18:02
  • Make class B a friend. Derive from A, but create objects of type B, not A. Make public calls in A if you need it. Use plain functions. Anything, but don't use OOP if you're going to break it. In general, code that works != code is ok. – Lyth Dec 09 '11 at 08:41

7 Answers7

6

In C and C++, it is generally undefined behaviour (read this as "illegal but allowed by compiler") to dereference an object of one type through a pointer or reference to an object of another type (with few exceptions like accessing via a pointer to a base class). This is called the strict aliasing rule.

Read more here: What is the strict aliasing rule?

Your code violates this rule by accessing an object of type A through the pointer to type B.


Note that generally the compiler isn't able to verify your static casts (in your case the C-cast is equivalent to a static_cast). If you aren't sure of the object type, dynamic_cast to verify if the cast is legal during runtime, as opposed to static_cast which is checked only at compile time and allows some incorrect casts.

Community
  • 1
  • 1
Kos
  • 70,399
  • 25
  • 169
  • 233
  • 1
    Actually "undefined behavior" should be read as "Illegal and NEVER DO THIS". – Mark B Dec 08 '11 at 14:59
  • Well that depends ]:>, but case of this specific issue, I absolutely agree. – Kos Dec 08 '11 at 15:03
  • Yeh i agree this, but is it really that unpredictable ? If the code remains like this and we do not do more inheritance of class A and B then the point of doing this will be to access protected or private member through such syntax without declaring an object explicitly – Orochi Dec 08 '11 at 18:10
  • You can just [#define protected public](https://github.com/TTimo/doom3.gpl/blob/master/neo/game/Game_local.h) and get the same result. The question is: Why would you even need to?! It either belongs to the interface (make it public) or doesn't (make it private). Don't over-complicate your code. – Kos Dec 08 '11 at 18:15
  • yeh i agree about over-complication But #define protected public will be very global kind of replacement :) I know this is prone to error but, is there any better option to do it without overriding all functions of class A which are declared virtual and must be overridden in B. By doing this i am saving a lot of redundant coding. i.e. B is not even declared and the (static) case is serving as a passage way to the protected member function. – Orochi Dec 08 '11 at 18:28
  • Long story: Don't make that thing private if it needs to be public. Short story: Redesign. – Kos Dec 08 '11 at 19:33
1

The danger is that having a pointer to A, you're normally not sure if it's B or C derived class instance.

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
  • This answer is too generalized, you should also explain why it works [I did not use c++ for a while, but from what I remember, I think it has something to do with `some_function()` not being `virtual`, and the fact that both A and B do not have any fields. – amit Dec 08 '11 at 14:31
  • 1
    As an aside, using C style casting with C++ code is really gross. Try looking into dynamic/static casting. – Darkenor Dec 08 '11 at 14:31
  • @amit, the problem is too general, the cast itself is bad, not necessarily with virtual function. The data pointed to doesn't necessarily meet expectations. – Michael Krelin - hacker Dec 08 '11 at 14:51
  • I agree the C Style cast and etc is bad programming practice. But the point is the declaration is there to provide an access to the protected member "without declaring an explicit object of B". Now one thing is clear "it is allowed and work". The real question is, is it dangerous to do such referencing for accessing a class with such a cast in order to escape the explicit declaration of B object. – Orochi Dec 08 '11 at 18:07
1

Possibly - depends on how the compiler arranges memory for objects.

C++ is a language that gives more than enough rope to hang yourself with and eveybody else in the room!

My point of view is that you should use casts as little as possible and only on rare occasions.

Ed Heal
  • 59,252
  • 17
  • 87
  • 127
1

One danger is that call_some_function can only call functions (or access members in general) in A. All access to a member of B will result in access outside of allocated memory, with probably disastrous consequences.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Yeh but for this specific scenario when the point of defining the class B is to access a protected member in A. Is it ok to do like that ? – Orochi Dec 08 '11 at 18:00
  • @Orochi In this case, using `B` as a proxy for `A` it might be ok. But usage like this should definitively an exception and not a rule. – Some programmer dude Dec 08 '11 at 18:46
1

It's undefined behavior, so anything could happen. In practice, you might get away with it as long as only single inheritance is involved, but in general, you can't count on it; a debugging implementation could, for example, generate code in B::call_some_function to ensure that the address passed as this corresponds to an object of type B that actually exists in the program.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • So that means is it "safe" if i do not have any member variables in B and i don't do more inheritance ? – Orochi Dec 08 '11 at 17:58
  • 1
    No. It means that you're likely to get away with it for the moment. – James Kanze Dec 08 '11 at 18:06
  • But what could go wrong if the motive is to access the private or protected member function only ? – Orochi Dec 08 '11 at 18:18
  • Anything the implementer wants to go wrong. That's what undefined behavior means. I already pointed out one possibility: a debugging version of the compiler could generate verification code. – James Kanze Dec 08 '11 at 18:48
1

In fact the code only appears to work. You've invoked undefined behavior by telling the compiler you have a B when you really only have an A (parent class). It could break at any time, even with the same compiler.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • That's interesting, do you think it will still have a chance to break even if the code remains same and nothing else is added to it ? – Orochi Dec 08 '11 at 20:44
0

The dengures is in scenario like this (and I'm sure some more):

class B: public A{
public:

    int local_var;

    void call_some_function(int params)
    {
        local_var = 5; // IN HERE.
        some_function(params); // Simple call to A::some_function and NOTHING MORE.
    }
};

In this case it will try to access an undefined var (local_var).

It's like:

struct A {
int i1;
int i2;
}

struct B {
int i1;
int i2;
int i3;
}

A a;
(B)a.i1 = 2;

it will work (most likely) but it's bad practice and basically undefined code.

Roee Gavirel
  • 18,955
  • 12
  • 67
  • 94