Thread: forward declaration

  1. #1
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853

    Thumbs up forward declaration

    I have read a few things about forward declaration from previous posts. I see that references and pointers can be used with forward declaration but not objects, probably due to the fact that the compiler won't know some details of the class (like the size).
    But this doesn't compile:
    Code:
    class A {
       void fun(B& bclass) {
           bclass.data;
       }
    };
    
    class B {
        int data;
        A hey;
    }
    The errors (gcc):
    error: Invalid use of undefined struct B
    error: forward declaration of struct B

    Is there a way to do what I want?

    Another question is this. Is dynamic_cast generally recommended to be used, or does it have to much an cost? Can its exception (wrong casting) be handled so the program doesn't crush?

    And finally, lets say you have a pointer to a class A. You point it on a derived class B : A. Can you determine with a function if the pointer points at a class of a or B?

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    class B;

    Put that before you declare class A
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    You still can't use members before they are declared. This works (modulo .data being a private member of B):
    Code:
    class B;
    
    class A {
        void fun(B& bclass);
    };
    
    class B {
        int data;
        A hey;
    };
    
    void A::fun(B& bclass) {
        bclass.data;
    }

  4. #4
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    Lol, I did put it but forgot to post it. That is why I named the topic "forward declaration". I ll edit it

  5. #5
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    Quote Originally Posted by tabstop View Post
    You still can't use members before they are declared. This works (modulo .data being a private member of B):
    Code:
    class B;
    
    class A {
        void fun(B& bclass);
    };
    
    class B {
        int data;
        A hey;
    };
    
    void A::fun(B& bclass) {
        bclass.data;
    }
    Thanx!

  6. #6
    Registered User
    Join Date
    Jan 2008
    Posts
    290
    Quote Originally Posted by C_ntua View Post
    Another question is this. Is dynamic_cast generally recommended to be used, or does it have to much an cost? Can its exception (wrong casting) be handled so the program doesn't crush?

    And finally, lets say you have a pointer to a class A. You point it on a derived class B : A. Can you determine with a function if the pointer points at a class of a or B?
    Generally you design your classes polymorphically so that you never have to cast down to a derived type, however in the rare cases where you do need to downcast, you can check the runtime type of the object before you perform the cast using 'typeid'.

    Some compilers require you to use a special flag to turn on RTTI though. Also notice that I included a virtual destructor in the super class. If you don't include at least one virtual function, typeid() probably won't report the actual runtime type of the object. (As you probably already know though, you should always include at least a virtual destructor)

    Code:
    #include <iostream>
    #include <typeinfo>
    
    class Super
    {
    public:
       virtual ~Super() {}
    };
    
    class Sub : public Super {};
    
    int main()
    {
       Super *obj = new Super;
       std::cout << typeid(*obj).name() << std::endl;
       
       if (typeid(*obj) == typeid(Super))
          std::cout << "Yes, this is a Super class." << std::endl;
       else
          std::cout << "No, this isn't a Super class." << std::endl;
       
       delete obj;
       
       obj = new Sub;
       std::cout << typeid(*obj).name() << std::endl;
       
       if (typeid(*obj) == typeid(Sub))
          std::cout << "Yes, this is a Sub class." << std::endl;
       else
          std::cout << "No, this isn't a Sub class." << std::endl;
       
       delete obj;
    }

  7. #7
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    Thanx, that will do the trick.
    Is it actually rare to downcast? I mean if you have a class Obj and derive from it class A: Obj and class B: Obj. Now, you want to have a list with both A and B objects. What other option do you have rather than having a Obj* pointer and down casting accordingly either to A* or B*? This is a common example I find (though for Java to be honest).

  8. #8
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Virtual functions? Normally you don't care which derived class instance you are using as long as it can - in its way - do everything which the base class promises.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  9. #9
    Registered User
    Join Date
    Jan 2008
    Posts
    290
    If you find yourself needing to downcast often, that's usually a sign of bad object-oriented design practices. Sometimes you do genuinely need to downcast, but you usually try to avoid this as much as possible.

    Maybe if you mentioned what your program is doing and why you are trying to downcast, someone could suggest a more appropriate polymorphic approach (if possible).

  10. #10
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    What I do is have a list of objects from which the user will choose something. If he chooses the wrong type of object then an error message should occur. If not then the data of the object should be accessed.
    Now that I think of it, I could have a virtual function getData() and return accordingly the data from the class, so I don't need to downcast at all. Like:
    Code:
    class A {
      virtual void getData() (int& data1, int& data2) {
         cout << "no data");
      }
    };
    class B: A {
      int dataB;
      void getData() (int& data1, int& data2) {
          data1 = dataB;
      }
    };
    
    class C: A {
        int dataC1, dataC2;
        void getData (int& data1, int& data2) {
           data1 = dataC1;
           data2 = dataC2;
      }
    };
    Though this code seems to have some downsides. Like what if every class has a different type of data?

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Such things are usually solved through templates, I think (classes having different data):

    Code:
    template<typename T> void HandleData(CMyClass<T>& data)
    {
        T data = data.GetData();
        // ...
    }
    It all depends on how you should use the data, I think.
    You could also use a virtual function IsValid to get a bool if the data is valid or not.
    Or GetData can throw an exception.

    Hope these suggestions help some.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Again, normally you wouldn't think about your classes as holders of data (with getters/setters, in which case it is hard to avoid knowing the exact type of things) rather than as performers of actions. Compare having a Mage with method Get/SetSpellDamage and a Warrior with method Get/SetSwordAttackDamage, vs both having a method (Attack/Upgrade) or something like that.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  13. #13
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    Hmm, I get your point of view. I ll follow what you say anon using equip() function to handle the data of my list<Item>.
    And yes the template should solve the rest of the problems, I suppose.

    Atm I am just building a D&D 4E character builder/arena fight, just to see the features of C++. The program is kind of easy so it could be done in a lot of ways. I just try to make it as OOP as possible and avoid from now bad programming techniques.

    I would like to ask though why is using virtual functions a better method than downcasting, since you have typeid() to access data safely without crashing the program with a dynamic_cast. More things to convince me

    Also, when a object of a certain class is created what exactly happens?
    1) Space is allocated for all its variables and pointers, including the this pointer.
    2) Space is allocated to store the address of the function in the class and its manual and default constructors/deconstructors.
    3) So, if the function is virtual, then the address of the derived class's function is stored in the place of the base class virtual function
    4) If an object is declared inside an object, then space is allocated with the above steps
    5) In run time, the constructor assigns values and anything else it wants
    6) Space is freed when the object goes out of scope. So I suppose the compiler finds where the scope ends and calls the deconstructor function there.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by C_ntua
    I would like to ask though why is using virtual functions a better method than downcasting, since you have typeid() to access data safely without crashing the program with a dynamic_cast.
    Conceptually, I like the example of a man entering a restaurant. He looks at the next table and sees someone eating a delicious meal. Then he calls the waiter over and says, "if that customer is having spaghetti, I want spaghetti, but if he is having lasagna, I want lasagna, but then if he is having...", reciting the menu until the point where the dish matches what the customer is eating. This is like the approach of using dynamic_cast to determine the correct type of MenuItem that the other customer has, and then ordering that one. The use of a virtual clone() function, on the other hand, is like the man saying to the waiter "I'll have what he's having!"
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by C_ntua View Post
    I would like to ask though why is using virtual functions a better method than downcasting, since you have typeid() to access data safely without crashing the program with a dynamic_cast. More things to convince me
    For one, more abstraction or encapsulation.
    Secondly, very likely, it's much faster.
    And thirdly, polymorphism was invented to be able to manipulate objects without knowing the exact type (even better with static polymorphism). In other words, polymorphism exists so that you shouldn't have to do downcasts!

    1) Space is allocated for all its variables and pointers, including the this pointer.
    Space is allocated for all member variables. "this" is not a member variable and thus no space is allocated for it. "this" is just a keyword that equals the address in memory where the instance of the class is stored. Basically it's just &myobj, where myobj is the instance of your class on the stack.

    2) Space is allocated to store the address of the function in the class and its manual and default constructors/deconstructors.
    No, functions are global. They do not belong to a specific instance. They are functions with an extra parameter for "this".
    Constructors are the same - they, too, are functions.

    3) So, if the function is virtual, then the address of the derived class's function is stored in the place of the base class virtual function
    Well, I suppose in a way.
    A usual approach to this (how virtual functions work are implementation defined!) is a v-table. Basically, the compiler stores a pointer to the correct function to be called for each instance of the class. When you create a Derived object, it stores the address of the Derived's function. The same applies if it's Base. Then Base's function is stored in the v-table.
    However, this isn't guaranteed since each compiler vendor can do as they want, just as long as it works.

    4) If an object is declared inside an object, then space is allocated with the above steps
    Yes, with the corrections. If object B is inside object A, then object B will be constructed while A is still constructing. When B finished constructing, only then will A finish constructing.

    5) In run time, the constructor assigns values and anything else it wants
    Yes, the constructor's job is to make sure that the object is ready-to-use after you finish creating it.

    6) Space is freed when the object goes out of scope. So I suppose the compiler finds where the scope ends and calls the deconstructor function there.
    Yes, this applies to any objects placed on the stack.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. how do you resolve this error?
    By -EquinoX- in forum C Programming
    Replies: 32
    Last Post: 11-05-2008, 04:35 PM
  2. Quantum Random Bit Generator
    By shawnt in forum C++ Programming
    Replies: 62
    Last Post: 06-18-2008, 10:17 AM
  3. failure to import external C libraries in C++ project
    By nocturna_gr in forum C++ Programming
    Replies: 3
    Last Post: 12-02-2007, 03:49 PM
  4. We Got _DEBUG Errors
    By Tonto in forum Windows Programming
    Replies: 5
    Last Post: 12-22-2006, 05:45 PM
  5. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM