CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 13 of 13
  1. #1
    Join Date
    May 1999
    Location
    Malaysia
    Posts
    99

    Some questions of Inheritance

    I define my classes as follow

    class A
    {
    };

    class B : public A
    {
    public:
    void functionB(void);
    };

    main()
    {
    A* objectB=new B;
    objectB->functionB(); // this should be OK

    A* objectA=new A;
    ((B*)objectA)->functionB(); // this is OK too, why?
    }

    suppose functionB() is the member of class B, it shouldnt be right to be called by A object. However I did it no problem, why?

    Any side effect if I do this?

  2. #2
    Join Date
    Jun 2002
    Location
    Germany
    Posts
    1,557
    An interesting example ctuyang.

    It is improper use of inheritance and, in my opinion, not OK. It will compile on some compilers and maybe run sometimes for some classes with certain functions and characteristics. But it is not a correct use of inheritance.

    The problem is that you have casted an object of type A into an object of type B. This can not work since the casted object (of true type A*) will not have all the members which it needs to be a B*.

    Study the following code example. You see that in the third function call, the behavior is maybe not what you expected.

    Chris.

    #include <iostream>
    using namespace std;

    class A
    {
    private:

    int m_na;

    public:

    A()
    {
    m_na = 1;
    }

    public:

    void functionA(void)
    {
    cout << m_na << endl;
    cout << "AAA" << endl;
    }
    };

    class B : public A
    {
    private:

    int m_nb;

    public:

    B()
    {
    m_nb = 2;
    }

    void functionB(void)
    {
    cout << m_nb << endl;
    cout << "BBB" << endl;
    }
    };

    int main(int argc, char* argv[])
    {

    A* object_true_A = new A;
    object_true_A->functionA(); // this is definitely OK

    B* objectB = new B;
    objectB->functionB(); // this should be OK

    A* objectA = new A;
    ((B*)objectA)->functionB(); // this is OK too, why?

    return 1;
    }

    You're gonna go blind staring into that box all day.

  3. #3
    Join Date
    Mar 2002
    Location
    Israel
    Posts
    187
    Hi,

    both of cases are not Ok. But there is difference between them.
    In first case You will get compilation error - something like
    'functionB is not a member of class A'.

    The second case is more complicated. Please imagine structs
    with only data, no functions like:
    Code:
    struct A
    {
    char ch;
    };
    
    struct B
    {
       struct A;
       ...
    };
    What will happen when You access memory after instance of A ?
    The result is undefined.
    It could be access violation/segmentation fault exception,
    You can overwrite useful data,
    it can be nothing wrong.

    Exactly the same will happen when You cast real A to B.
    The code will pass compilation, but run-time result is undefined.

  4. #4
    Join Date
    Jun 2002
    Location
    Dover, England
    Posts
    28
    Hi,

    This is actually nothing to do with inheritance, but rather a problem with casting. The problem is basically the need for C++ to be back-compatible with C. In C there is only one way of expressing a cast from one type to another, even though there are several different types of casting.
    When C++ was created, these different types of casting were given their own names, creating the following:

    static_cast<newType>( ... )
    dynamic_cast<newType>( ... )
    reinterpret_cast<newType>( ... )
    const_cast<newType>( ... )

    Where '...' specifies what is to be cast, and 'newType' is the type to cast to.

    Unfortunately, in order to make C++ compatible with legacy C code, the old style,
    (newType)( ... )

    was kept. This can lead to problems, as the old style casts can mean any of the four types. The compiler simply chooses the type which allows the code to be compiled.

    In your code, in the line

    ((B*)objectA)->functionB(); // this is OK too, why?

    the compiler chooses to use reinterpret_cast. This means it considers 'objectA' to be of type B* regardless of what it actually is in reality. As it now thinks it is B*, it can call functionB() on it. However, the effect this will actually have when you run your program will likely be unpredictable and could have extremely nasty side-effects. And I mean extremely nasty. As an example of quite how nast reinterpret_cast can be, try compiling the following:

    int* number = new int;
    ((B*)number)->function();

    which will compile, but god knows what it would do!

    I would recommend that you never use old style casts, but pick the one you need from the list above. I would also recommend that you never use reinterpret_cast or const_cast either.

    Hope this helps,

    Pete

  5. #5
    Join Date
    Jun 2002
    Location
    Letchworth, UK
    Posts
    1,020
    I'm surprised that

    objectB->functionB(); // this should be OK

    builds with a C++ compiler. I can understand it building with a C compiler but not with a C++ compiler. With the older C compilers, members were just offsets so you could do something silly like

    struct AA
    {
    int a;
    int b;
    } abc;

    struct BB
    {
    int c;
    } def;

    def.b = 9;

    This shouldn't work on the newer compilers but some of the pre-ANSI C compilers will take it.

    As Pete Bourner says, use the new style casts. It is a lot more typing but it makes you think about what you are doing instead of just forcing the coercion.
    Succinct is verbose for terse

  6. #6
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    They're also easier to find after you've done the redesign to remove the need for them.

    Code:
    
    A* objectB=new B;
    objectB->functionB(); // this should be OK
    
    Absolutely not - functionB is not a member of A, so this should not compile.
    Code:
    
    A* objectA=new A;
    ((B*)objectA)->functionB(); // this is OK too, why?
    
    A lucky accident from the way the compiler works. Don't rely on it - it's even more wrong than the previous (if you can have "even more wrong").
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


  7. #7
    Join Date
    Jun 2002
    Location
    Germany
    Posts
    1,557
    Dear ctyuang,

    All of the comments up to this point are correct. I like to think of it this way: Although you can cast an object to or use an object as less than it is (B calls stuff from A), you can can never cast an object to more than it is (A calls stuff from B).

    It is important for you to understand so I have supplied more example code, also addressing the good comments up to this point.

    By the way, the original code that you suppled can not compile. My original comments were pertaining to the cast of A* to B*.

    OK then, the sample code:

    // This is definitely OK
    A* object_true_A = new A;
    object_true_A->functionA();

    // This definitely OK
    B* object_true_B = new B;
    object_true_B->functionB();

    // This is also OK since B can access "all of A".
    object_true_B->functionA();

    A* object_A_casted_to_B = new A;

    // Now uses modern cast.
    // It will compile, but it's absolutely not OK!!!
    reinterpret_cast<B*>(object_A_casted_to_B)->functionB();

    Chris.

    You're gonna go blind staring into that box all day.

  8. #8
    Join Date
    May 1999
    Location
    Malaysia
    Posts
    99

    Re: dude_1967

    thank you, this example reveals clearly my problem. Its true the following code doesnt work:

    // List 1
    /////////
    A* objectA = new A;
    ((B*)objectA)->functionB();
    // functionB() is not A object member.

    however this code work.

    // List 2
    //////////
    A* objectA = new B;
    ((B*)objectA)->functionB();
    // function B is B object member

    List 2 code work because objectA is just a pointer, it point to its children object, which is (*objectA).

  9. #9
    Join Date
    Jun 2002
    Location
    Dover, England
    Posts
    28
    // List 2
    //////////
    A* objectA = new B;
    ((B*)objectA)->functionB();
    // function B is B object member


    The above code works because the casting is now performing a dynamic_cast. This is used to convert from base class pointers to derived class pointers. In the example above, although 'objectA' is of type A*, it actually points to an object of type B, which is derived from A. Therefore, casting objectA to B* using a dynamic_cast is a valid operation.

    I think this shows the importance of using the new, explicit cast syntax, as this would make it clear what the above is doing. In this case you should write:

    A* objectA = new B;
    (dynamic_cast<B*>( objectA ))->functionB();


    Pete

  10. #10
    Join Date
    Mar 2002
    Location
    Israel
    Posts
    187
    The code like

    A* a = new A;
    ((B*)a)->functionB();

    does not cause a crash becouse of the same reason as follow code won't cause crash:

    Code:
    class B
    {
    public:
      void fun() {}
    };
    
    B* b = 0;
    b->fun();
    Addressing to an object is an high-level abstraction. Compiler
    simply put the address of 'b' as function parameter.
    So far this->not_access_illegal_address_in_memory, Your
    application will continue to run.
    Last edited by Alexis Moshinsky; July 8th, 2002 at 04:25 AM.

  11. #11
    Join Date
    May 1999
    Location
    Malaysia
    Posts
    99

    Re : Pete <dynamic_cast error>

    I tried dynamic_cast. Its too bad, compile error!

    I have tried static_cast, and reinterpret_cast, they are OK, but not dynamic_cast and const_cast.

    I will do further reading on casting.

    Thank you to all of you. HAVE A NICE DAY!!!!

  12. #12
    Join Date
    Jun 2002
    Location
    Dover, England
    Posts
    28
    A* objectA = new B;
    (dynamic_cast<B*>( objectA ))->functionB();


    Whoops - the dynamic_cast doesn't work does it? My mistake - you can only use dynamic_cast to convert from classes which have virtual functions ( also called polymorphic classes ).
    In the example given here, A is not polymorphic as it has no functions at all, let all virtual ones.

    static_cast will work, and in this case will be valid as objectA really a B. reinterpret_cast will also work for the same reason, but should still be avoided as there is no compile time protection against bad usage.

    Thanks for pointing out my error,

    Pete.

  13. #13
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    If you're using VC++, then you have to turn on RTTI before you can use dynamic_cast. (Project/Settings... C/C++ tab, select "C++ Language" in teh drop down, and put a check mark beside "Enable run-time type information").
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


Posting Permissions

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





Click Here to Expand Forum to Full Width

Featured