locked
Derived C++ Class RRS feed

  • Question

  • I hope I can explain this without getting too complicated. I have a scene made up of facet models. To define the scene I use three classes: CScene, CModel and CComponent. The CScene class contains a list of CModel's, each model is comprised of a list of components within the model (tire, turret, etc) and each component contains a list of facets. I have all of these in a library. I would like to derive a new Cmodel class and a new CComponent class; I'll call them CNewModel and CNewComponent. My problem is how to get CModel to use this new derived class rather than the CComponent that it knows about. I've tried a couple of methods. CModel declares a variable CComponent *component. If I declare a new variable in CNewModel such as CNewComponent *newComponent, that doesn't work because whenever I try to access the base class, CModel reverts back to it's own copy of CComponent which is now undefined. If in CNewModel I try something like component = new CNewComponent, so that the component variable in CModel points to my new derived component, the compiler thinks that component is still a CComponent class instead of CNewComponent, so only the CComponent operations are available. I guess after thinking it over, neither of these outcomes should surprise me. I'm trying to figure out if there is a way around this. Maybe I have missed something simple, or it just can't be done. Any ideas? Thank you.
    Wednesday, January 26, 2011 4:51 PM

Answers

  •  
    Are you using virtual functions? This is precisely the problem that virtual functions are intended to solve. The mechanism is called polymorhism.
     
    The methods of CComponent should be virtual, and overridden in CNewComponent.
     

    David Wilkinson | Visual C++ MVP
    • Marked as answer by JimmyG Friday, February 4, 2011 2:50 PM
    Wednesday, January 26, 2011 5:10 PM
  • Maybe the following example will help:

     

    #include <iostream>
    using std::cout;
    using std::endl;
    
    class CComponent
    {
     public:
      virtual void func() { cout << "CComponent::func()" << endl; } // virtual keyword means that pointers of type CComponent will call the actual object version of the function (maybe CComponent::func, maybe CNewComponet::func)
    };
    
    class CNewComponent : public CComponent
    {
     public:
      virtual void func() { cout << "CNewComponent::func()" << endl; } // child class overrides func
      void new_func() { cout << "CNewComponent::new_func()" << endl; } // child class also offers some new functionality
    };
    
    class CModel
    {
     public:
      CModel(CComponent * c) :
       c(c)
      {
      }
    
      void do_component_func()
      {
        c->func();
      }
      
     private:
      CComponent * c; // base class points to a Component object (which could be of type Component or NewComponent)
    };
    
    
    
    class CNewModel : public CModel
    {
     public:
      CNewModel(CNewComponent * c) :
       CModel(c),
       new_c(c)
      {
      }
    
      void do_new_component_new_func()
      {
        new_c->new_func();
      }
    
     private:
      CNewComponent * new_c; // child class knows that the component is really a NewComponet, and so can access things like "new_func"
      // note that CModel::c and CNewModel::new_c both point to the same object. When c is used (for instance in CModel functions),
      // you will only have access to functions declared in CComponent (however virtual functions will still call the most-derived version
      // of that function). When new_c is used, you will have access to all the CComponent stuff, but also any new functions you added to
      // CNewModel.
    };
    
    
    
    int main()
    {
      CNewModel m(new CNewComponent);
      m.do_component_func(); // calls CNewComponents version because func is virtual
      m.do_new_component_new_func();
    }
    

    • Marked as answer by JimmyG Friday, February 4, 2011 2:53 PM
    Wednesday, January 26, 2011 5:31 PM
  • No, none of them are virtual so I'll try that. However, none of the methods CComponent class have been overridden, except for the contructor. Only more variables have been added. BTW, sorry my question was so hard to read. For some reason it lost all of my formatting.
    Note that CModel can only access those methods of CNewComponent that it knows about, which are the methods that are in CComponent. It cannot access methods of CNewComponent that are not in CComponent.
     

    David Wilkinson | Visual C++ MVP
    • Marked as answer by JimmyG Friday, February 4, 2011 2:50 PM
    Wednesday, January 26, 2011 5:47 PM
  • Just a quick note to Fredick_V's example: when deriving classes and using polymorphism, one should declare a virtual destructor in the classes at the top of their hierarchy, to avoid obscure bugs.

    class CComponent
    {
     public:
     virtual void func() { cout << "CComponent::func()" << endl; }
     virtual ~CComponent() { }
    };
    

    Same thing for CModel, etc.

     

    • Marked as answer by JimmyG Friday, February 4, 2011 2:53 PM
    Wednesday, January 26, 2011 7:06 PM
  • My code didn't use "copy constructor".  "Copy constructor" is usually the term for when you create an object that is the copy of another object of the same class, e.g.:

     

    class Foo
    {
     public:
      Foo() :
       x(0)
      {
      }
    
      Foo(Foo const & other) :  // <--- copy constructor
       x(other.x)
      {
      }
    
     private:
      int x;
    };
    
    

    The code I posted previously actually had a constructor that was taking arguments (of a different class) that it would use to initialize its members.

     

    I think you intended your code to be pseudo-code, but I marked it up and wrote some comments.

    class CComponent
    {
     public:
      CComponent()
      {
      }
      
      virtual void CopyComponent(CComponent src)
      {
        component_stuff = src.component_stuff; // component_stuff is undeclared
      }
    };
    
    
    class CNewComponent : public CComponent
    {
     public:
      CNewComponent()
      {
      }
      
      virtual void CopyComponent(CComponent *src)
      {
        component_stuff = src.component_stuff;
        new_variable = 10;
      }
      
      int new_variable;
      void new_function(); // new_function is never defined
    };
    
    class CModel
    {
     public:
      CModel()
      {
      }
      
      virtual void CopyModel(CModel src)
      {
        model_stuff = src.model_stuff; // model_stuff is undeclared
        // The following creates a local variable named c.
        // Note, this local variable has the same name as your class member variable named c. This "hides" the member variable.
        // You create an array of components, OK. But by assigning it to the local variable you will lose any way to access those objects. This is because the pointer itself will be destroyed at the end of the function.
        // It seems more likely you just ment to say c = ... and not declare a new pointer.
        // num_components is undelcared
        CComponent *c = new CComponent [src.num_components];
      }
     public:
      CComponent * c;
    };
    
    class CNewModel : public CModel
    {
     public:
      CNewModel()
      {
      }
      
      virtual void CopyModel(CModel src)
      {
        // See comments in CModel::CopyModel
        model_stuff = src.model_stuff;
        CNewComponent *new_c = new CNewComponent [src.num_components];
      }
     public:
      CNewComponent * new_c;
    };
    
    int main()
    {
      CModel *model = new CModel;
      CNewModel *newModel = new CNewModel;
      CNewComponent *component;
      newModel->CopyModel(model);
      component = &newModel->new_c[5]; // sorry I got lost here, what are you trying to do that is not working?
      component->new_variable = 10;
      component->newfunction();
    }
    

    • Marked as answer by JimmyG Friday, February 4, 2011 2:54 PM
    Wednesday, January 26, 2011 11:42 PM
  • As for platform, this code has to run on multiple environments, including MFC on WINxx, and as a non-GUI MATLAB mex function on WIN, MAC and Linux. The classes were originally written using three linked lists, but I recently changed them to arrays for two reasons. One, I decided I did it the hard way (it probably made sense at one time). The second is because the scenes are also being displayed in OpenGL. To speed things up I wanted to use Vertex Pointers which requires the data to be contiguous, which nixed using linked lists. Which leads to another question since someone mentioned using the STL array. If I use STL am I guaranteed that the data will be contiguous?
    In the latest C++ standard, the elements of std::vector are guaranteed to be contiguous. In earlier versions this was not the case, but to my knowledge all known implementations of std::vector use contiguous storage.
     
    See, for example:
     
    http://groups.google.com/group/comp.lang.c++/msg/408d058c256d7699
     

    David Wilkinson | Visual C++ MVP
    • Marked as answer by JimmyG Friday, February 4, 2011 2:52 PM
    Thursday, January 27, 2011 5:37 PM
  • Fredrick, I think there is a flaw in your sample code. In the function CopyModel you have a line that says components = new_components; This doesn’t work because this is an array of objects, but the objects are two different sizes. For example, let’s say that CComponent contains a single integer so that each entry is four bytes. Now let’s suppose that CNewComponent contains two integers so that each entry is now eight bytes. The problem with setting component equal to new_component is that when functions in the CComponent base class traverse the array they only increment the offset by four bytes instead of eight because they still think it is a CComponent class instead of a CNewComponent class. I may have to try to virtual base class next that Dave suggested. I wanted to see if I could get this method working first because it seemed easier. Thanks, Jim
    Yes, as I mentioned before, if you want a heterogeneous collection, the collection must hold base class pointers, not objects. In this case, CComponent* Or if you have introduced an interface IComponent, then IComponent*.
     
    Even better if you use shared_ptr<CComponent>, or shared_ptr<IComponent>, which will take care of lifetime issues.
     

    David Wilkinson | Visual C++ MVP
    • Marked as answer by Jesse Jiang Wednesday, February 2, 2011 8:30 AM
    Thursday, January 27, 2011 10:38 PM
  • Fredrick, I think there is a flaw in your sample code. In the function CopyModel you have a line that says components = new_components; This doesn’t work because this is an array of objects, but the objects are two different sizes. For example, let’s say that CComponent contains a single integer so that each entry is four bytes. Now let’s suppose that CNewComponent contains two integers so that each entry is now eight bytes. The problem with setting component equal to new_component is that when functions in the CComponent base class traverse the array they only increment the offset by four bytes instead of eight because they still think it is a CComponent class instead of a CNewComponent class. I may have to try to virtual base class next that Dave suggested. I wanted to see if I could get this method working first because it seemed easier. Thanks, Jim

    Yes you are right that code won't work.  Rob__ offers some good advice.  How much control over CModel do you have?  One problem you have is that you have two datas, CModel's components and CNewModel's new_components.  C++ lets us have virtual function overloading, but not virtual data overloading.  Some options you could try are:
    - Ever member function in CModel that interacts with components must be made virtual and overriden in CNewModel to work with the new_components.  In this manner we essentially abandon use of CModel::components when the object is actually a CNewModel.

    - Have CModel hold own the components, but make it do so by pointer so that the actual components objects could actually be CComponents or CNewComponents.

    - Whenever CModel access's the components, have it do so through a virtual function.  This way the virtual function can properly get CModel::components when the object is CModel and get CNewModel::new_components when the object is a CNewModel.

    • Marked as answer by Jesse Jiang Wednesday, February 2, 2011 8:30 AM
    Thursday, January 27, 2011 11:35 PM
  • I have total control over Cmodel since I wrote it.  I’m starting to think that Fredrick’s second option, which is to have Cmodel contain an array off CComponent pointers rather than just Ccomponent’s makes the most sense.  This is essentially what Dave suggested above. 

    But I started thinking maybe I’m taking the wrong approach in the first place, so maybe I need to step back.

    In my original working version I had one CModel and CComponent class and everything was self-contained.  I recently made a major change and in the process I decided it be more object-oriented to create a base class and then extend it to add functionality.  But I didn’t realize how difficult it would be.

    The only difference between CComponent and CNewComponent is that I added a class called CQuadTree, like this:

    Class CNewComponent : CComponent public
    {
      CQuadTree *quadtree;
    };

    Otherwise, they are identical.  The quad tree just takes the facet data and sorts it and traverses the tree when I render the data.  The base classes are simply for holding and displaying the data.

    So I could just put the quad_tree back in the base class and solve the problem. 

    But now I am wondering if either approach is the best approach, since the quad_tree just acts on the data.  Should the CComponent contain a quad_tree, or should the quad_tree contain the data (or neither)?  I could do something like (assume the model and it’s components are already defined):

    CModel *model;
    CQuadTree *quad_tree;
    
    quad_tree = new CQuadTree [num_components];
    
    for (i=0; i<num_components; i++)
      quad_tree[i].CreateTree (&model->components[i]);
    
    // And to use it:
    
    for (i=0; i<num_components; i++)
      quad_tree[i].Render();
    

    The only drawback is that the quad_tree has to depend on the data being in CComponent because it is sorting the data using an index array.  If the model should get deleted for some reason (which shouldn’t happen unless I screw up), then it will be acting on garbage.
       
    So I’m not sure now what makes the most sense:

    1. Stick the quad tree back in the base class and be done with it.
    2. Use the array of component pointers as just suggested
    3. Do what I just mentioned with the array of quad_tree’s
    4. Maybe none of the above because there is a better approach if anyone has an idea.

    Thanks again.

    • Marked as answer by Jesse Jiang Wednesday, February 2, 2011 8:30 AM
    Friday, January 28, 2011 1:49 AM

All replies

  •  
    Are you using virtual functions? This is precisely the problem that virtual functions are intended to solve. The mechanism is called polymorhism.
     
    The methods of CComponent should be virtual, and overridden in CNewComponent.
     

    David Wilkinson | Visual C++ MVP
    • Marked as answer by JimmyG Friday, February 4, 2011 2:50 PM
    Wednesday, January 26, 2011 5:10 PM
  • No, none of them are virtual so I'll try that. However, none of the methods CComponent class have been overridden, except for the contructor. Only more variables have been added. BTW, sorry my question was so hard to read. For some reason it lost all of my formatting.
    Wednesday, January 26, 2011 5:28 PM
  • Maybe the following example will help:

     

    #include <iostream>
    using std::cout;
    using std::endl;
    
    class CComponent
    {
     public:
      virtual void func() { cout << "CComponent::func()" << endl; } // virtual keyword means that pointers of type CComponent will call the actual object version of the function (maybe CComponent::func, maybe CNewComponet::func)
    };
    
    class CNewComponent : public CComponent
    {
     public:
      virtual void func() { cout << "CNewComponent::func()" << endl; } // child class overrides func
      void new_func() { cout << "CNewComponent::new_func()" << endl; } // child class also offers some new functionality
    };
    
    class CModel
    {
     public:
      CModel(CComponent * c) :
       c(c)
      {
      }
    
      void do_component_func()
      {
        c->func();
      }
      
     private:
      CComponent * c; // base class points to a Component object (which could be of type Component or NewComponent)
    };
    
    
    
    class CNewModel : public CModel
    {
     public:
      CNewModel(CNewComponent * c) :
       CModel(c),
       new_c(c)
      {
      }
    
      void do_new_component_new_func()
      {
        new_c->new_func();
      }
    
     private:
      CNewComponent * new_c; // child class knows that the component is really a NewComponet, and so can access things like "new_func"
      // note that CModel::c and CNewModel::new_c both point to the same object. When c is used (for instance in CModel functions),
      // you will only have access to functions declared in CComponent (however virtual functions will still call the most-derived version
      // of that function). When new_c is used, you will have access to all the CComponent stuff, but also any new functions you added to
      // CNewModel.
    };
    
    
    
    int main()
    {
      CNewModel m(new CNewComponent);
      m.do_component_func(); // calls CNewComponents version because func is virtual
      m.do_new_component_new_func();
    }
    

    • Marked as answer by JimmyG Friday, February 4, 2011 2:53 PM
    Wednesday, January 26, 2011 5:31 PM
  • No, none of them are virtual so I'll try that. However, none of the methods CComponent class have been overridden, except for the contructor. Only more variables have been added. BTW, sorry my question was so hard to read. For some reason it lost all of my formatting.
    Note that CModel can only access those methods of CNewComponent that it knows about, which are the methods that are in CComponent. It cannot access methods of CNewComponent that are not in CComponent.
     

    David Wilkinson | Visual C++ MVP
    • Marked as answer by JimmyG Friday, February 4, 2011 2:50 PM
    Wednesday, January 26, 2011 5:47 PM
  • Just a quick note to Fredick_V's example: when deriving classes and using polymorphism, one should declare a virtual destructor in the classes at the top of their hierarchy, to avoid obscure bugs.

    class CComponent
    {
     public:
     virtual void func() { cout << "CComponent::func()" << endl; }
     virtual ~CComponent() { }
    };
    

    Same thing for CModel, etc.

     

    • Marked as answer by JimmyG Friday, February 4, 2011 2:53 PM
    Wednesday, January 26, 2011 7:06 PM
  • Thank you all. I think the main difference between what I am doing and Fredrick's code is he is using a copy constructor, and maybe that is what makes the difference. I need to create an array of classes. I took Fredrick's code and modified it. This is basically what I am doing now. I need to make a copy of a scene in order to do rotations, sorting and then rendering the scene. Is the only difference because of the copy constructor? class CComponent { public: CComponent () { }; virtual void CopyComponent (CComponent src) { component_stuff = src.component_stuff; }; }; class CNewComponent : public CComponent { public: CNewComponent () { }; virtual void CopyComponent (CComponent *src) { component_stuff = src.component_stuff; new_variable = 10; }; int new_variable; void new_function (); }; class CModel { public: CModel() { }; virtual void CopyModel (CModel src) { model_stuff = src.model_stuff; CComponent *c = new CComponent [src.num_components]; }; public: CComponent * c; }; class CNewModel : public CModel { public: CNewModel() { }; virtual void CopyModel (CModel src) { model_stuff = src.model_stuff; CNewComponent *new_c = new CNewComponent [src.num_components];}; public: CNewComponent * new_c; }; int main() { CModel *model = new CModel; CNewModel *newModel = new CNewModel; CNewComponent *component; newModel->CopyModel (model); component = &newModel->new_c[5]; component->new_variable = 10; component->newfunction(); }
    Wednesday, January 26, 2011 7:26 PM
  • Why does this keep losing my formatting? Once I find out I will re-submit so that you can read it. Thanks.
    Wednesday, January 26, 2011 7:28 PM
  • On 26/01/2011 20:28, JimmyG wrote:

    Why does this keep losing my formatting?

    It sometimes happens to me, too, using the NNTP Community Bridge and Thunderbird.

    Don't know the reason why...

    Giovanni

    Wednesday, January 26, 2011 9:42 PM
  • My code didn't use "copy constructor".  "Copy constructor" is usually the term for when you create an object that is the copy of another object of the same class, e.g.:

     

    class Foo
    {
     public:
      Foo() :
       x(0)
      {
      }
    
      Foo(Foo const & other) :  // <--- copy constructor
       x(other.x)
      {
      }
    
     private:
      int x;
    };
    
    

    The code I posted previously actually had a constructor that was taking arguments (of a different class) that it would use to initialize its members.

     

    I think you intended your code to be pseudo-code, but I marked it up and wrote some comments.

    class CComponent
    {
     public:
      CComponent()
      {
      }
      
      virtual void CopyComponent(CComponent src)
      {
        component_stuff = src.component_stuff; // component_stuff is undeclared
      }
    };
    
    
    class CNewComponent : public CComponent
    {
     public:
      CNewComponent()
      {
      }
      
      virtual void CopyComponent(CComponent *src)
      {
        component_stuff = src.component_stuff;
        new_variable = 10;
      }
      
      int new_variable;
      void new_function(); // new_function is never defined
    };
    
    class CModel
    {
     public:
      CModel()
      {
      }
      
      virtual void CopyModel(CModel src)
      {
        model_stuff = src.model_stuff; // model_stuff is undeclared
        // The following creates a local variable named c.
        // Note, this local variable has the same name as your class member variable named c. This "hides" the member variable.
        // You create an array of components, OK. But by assigning it to the local variable you will lose any way to access those objects. This is because the pointer itself will be destroyed at the end of the function.
        // It seems more likely you just ment to say c = ... and not declare a new pointer.
        // num_components is undelcared
        CComponent *c = new CComponent [src.num_components];
      }
     public:
      CComponent * c;
    };
    
    class CNewModel : public CModel
    {
     public:
      CNewModel()
      {
      }
      
      virtual void CopyModel(CModel src)
      {
        // See comments in CModel::CopyModel
        model_stuff = src.model_stuff;
        CNewComponent *new_c = new CNewComponent [src.num_components];
      }
     public:
      CNewComponent * new_c;
    };
    
    int main()
    {
      CModel *model = new CModel;
      CNewModel *newModel = new CNewModel;
      CNewComponent *component;
      newModel->CopyModel(model);
      component = &newModel->new_c[5]; // sorry I got lost here, what are you trying to do that is not working?
      component->new_variable = 10;
      component->newfunction();
    }
    

    • Marked as answer by JimmyG Friday, February 4, 2011 2:54 PM
    Wednesday, January 26, 2011 11:42 PM
  • The formatting of my code seems to keep getting messed up, so I will try this at home.
    
    Yes, the previous code was somewhat pseudo-code. I was trying to simplify it, but maybe I simplified it too much. This code is closer to what I am trying to do. 
    
    I'm not sure how to apply Fredrick's example of passing a class into the contructor when I need to initialize
    an array of either CComponent or CNewComponent. In Fredrick's example he was only defining a single variable. Thanks again. This is driving me crazy.
    
     
    
    class CComponent
    {
    public:
     CComponent () { };
    
     virtual void CopyComponent (CComponent *src) 
     { 
      num_facets = src->num_facets;
     
     facets = new float (num_facets);
      for (i=0; i<num_facets; i++)
        facet[i] = src->facet[i];};
    
     float *facets;
     int num_facets;
     void ComponentFunction();
    };
    
     
    
    class CNewComponent : public CComponent
    {
    public:
    
     CNewComponent () { };
     virtual void CopyComponent (CComponent *src) 
     {
      num_facets = src->num_facets;
      facets = new float (num_facets);
      for (i=0; i<num_facets; i++)
       facet[i] = src->facet[i];
    
      new_variable = 10;
      }
    
     int new_variable;
    
     void newComponentFunction ();
    };
     
    
    class CModel
    {
    public:
    
     CModel() { };
     
     virtual void CopyModel (CModel *src) 
     { 
      num_components = src->num_components;
      components = new CComponent [num_components];
    
      for (i=0; i<num_components; i++)
       components[i].CopyComponents (&src->components[i]);
     }
    
    public:
    
     int num_components;
     CComponent *components; 
    
    };
     
    
    class CNewModel : public CModel
    {
    public:
    
     CNewModel() { };
    
     virtual void CopyModel (CModel *src) 
     { 
      num_components = src->num_components;
      new_components = new CComponent [num_components];
    
      for (i=0; i<num_components; i++)
       new_components[i].CopyComponents (&src->components[i]);
    
      new_data = 20;
     }
    
     void newModelFunction (int data) {new_data = data;};
    
    public:
     int new_data;
     CNewComponent * new_components; 
    };
    
    
    int main()
    {
     CModel  *model = new CModel;
     CNewModel *newModel = new CNewModel;
    
     // Assume that model has already been initialized with data.
     // I need to make of copy of the original model data into the new model.
    
     newModel->CopyModel (model);
    
     // Now I try to access variables and functions from both CComponent and CNewComponent
    
     newModel->component[5].ComponentFunction();
     newModel->component[3].new_variable = 100;
     newModel->component[1].newComponentFunction();
    }
    
    
    Thursday, January 27, 2011 3:22 AM
  • Yes, the previous code was somewhat pseudo-code. I was trying to simplify it, but maybe I simplified it too much. This code is closer to what I am trying to do.
     
    I'm not sure how to apply Fredrick's example of passing a class into the contructor when I need to initialize an array of either CComponent or CNewComponent. In Fredrick's example he was only defining a single variable. Thanks again. This is driving me crazy.
    You should try to structure yout code so that everything the CModel wants to do to CComponent or CNewComponent is done by calling a virtual method. Then CModel can contain an array of CComponent* (not CComponent), some of which are actually CNewComponent*. Then polymorphism will work for you.
     

    David Wilkinson | Visual C++ MVP
    Thursday, January 27, 2011 10:53 AM
  • Yes, the previous code was somewhat pseudo-code. I was trying to simplify it, but maybe I simplified it too much. This code is closer to what I am trying to do.
     
    I'm not sure how to apply Fredrick's example of passing a class into the contructor when I need to initialize an array of either CComponent or CNewComponent. In Fredrick's example he was only defining a single variable. Thanks again. This is driving me crazy.
    You should try to structure yout code so that everything the CModel wants to do to CComponent or CNewComponent is done by calling a virtual method. Then CModel can contain an array of CComponent* (not CComponent), some of which are actually CNewComponent*. Then polymorphism will work for you.
     

    David Wilkinson | Visual C++ MVP

    Yes...
    What you're saying is to create a "pure virtual baseclass" (i.o.w. an interface) to give to CModel, derive both variants from that.
    In that case there won't be any confusion.
    Drawback of that is there wont be much code-reuse between 2 instances of the variants, for that you could add a 2th layer of inheretance.
    (derive a baseclass that contains some common implementation from the interface, and both variants from that)
    Note that the __interface keyword is non-standard, if that is of any concern to you (forgive me the possibly wrong syntax).

    Kind regards,
    Rob
    www.robtso.nl

     



    Thursday, January 27, 2011 12:24 PM
  • What you're saying is to create a "pure virtual baseclass" (i.o.w. an interface) to give to CModel, derive both variants from that.
    In that case there won't be any confusion.
    Drawback of that is there wont be much code-reuse between 2 instances of the variants, for that you could add a 2th layer of inheretance.
    (derive a baseclass that contains some common implementation from the interface, and both variants from that)
    Note that the __interface keyword is non-standard, if that is of any concern to you (forgive me the possibly wrong syntax).

    Yes, a pure virtual (interface) base class would be better.


    David Wilkinson | Visual C++ MVP
    Thursday, January 27, 2011 1:10 PM
  • One thing that concerns me with these virtual functions is what kind of performance penalty will there be (if any). This is a laser model and I've had to render scenes with up to ten million facets. If a plane is doing a flyby, the rendering code may get called hundreds of times. Of course, I'm not sure I have much choice.
    Thursday, January 27, 2011 2:57 PM

  • new_components = new CComponent [num_components];

    ...

    for (i=0; i<num_components; i++)
    new_components[i].CopyComponents (&src->components[i]);

    Here you are creating CComponent objects and point at them with a CNewComponent pointer.  This is not allowed.  While a CNewComponent is a CComponent (it has all the parts of a CComponent), a CComponent is not a complete CNewComponent (for instance, it is missing the "new_variable").  So you can point at CNewComponents with CComponent pointers, but not the other way around, because it would allow you to access things which don't really exist.

    I have updated your code with some comments:


    class CComponent
    {
     public:
      CComponent() { }
    
      virtual void CopyComponent(CComponent *src)
      {
        num_facets = src->num_facets;
        facets = new float[num_facets];
        for (int i=0; i<num_facets; i++)
        {
          facets[i] = src->facets[i];
        }
      }
    
      float *facets;
      int num_facets;
      void ComponentFunction() {}
    };
    
     
    
    class CNewComponent : public CComponent
    {
     public:
    
      CNewComponent() { }
    
      virtual void CopyComponent(CComponent *src)
      {
        CComponent::CopyComponent(src); // do the copy like my parent class would
        // Add new copy stuff here:
        new_variable = 10; // since src is a CComponents, and doesn't actually have a new_variable value to copy, we just make some value up
      }
    
      virtual void CopyComponent(CNewComponent *src)
      {
        CComponent::CopyComponent(src); // do the copy like my parent class would
        // Add new stuff here:
        new_variable = src->new_variable; // we can actually copy the CNewComponents value
      }
    
      int new_variable;
    
      void newComponentFunction() { }
    };
     
    
    class CModel
    {
     public:
      CModel() { }
      
      virtual void CopyModel(CModel *src)
      {
        num_components = src->num_components;
        components = new CComponent [num_components];
    
        for (int i=0; i<num_components; i++)
        {
          components[i].CopyComponent(&src->components[i]);
        }
      }
    
      virtual CComponent * GetComponents() { return components; }
    
     public:
      int num_components;
      CComponent *components;
    };
     
    
    class CNewModel : public CModel
    {
    public:
      CNewModel() { };
    
      virtual void CopyModel (CModel *src)
      {
        num_components = src->num_components;
        new_components = new CNewComponent [num_components]; // note we create CNewComponets here instead of CComponents (thus we cannot use the trick in CNewComponent of calling the parent version)
        for (int i=0; i<num_components; i++)
        {
          new_components[i].CopyComponent(&src->components[i]);
        }
    
        components = new_components;
        // Above we set the components pointer we inherited from our parent class to the objects we just created.
        // Notice both new_components and components point to the same set of objects.
        // By doing this, when we work with functions from the CModel class, they will have "Components" to work with (really NewComponents)
    
        new_data = 20; // here our src doesn't have new_data to copy, so we just make some value up
      }
    
      virtual void CopyModel (CNewModel *src)
      {
        num_components = src->num_components;
        new_components = new CNewComponent [num_components]; // note we create CNewComponets here instead of CComponents (thus we cannot use the trick in CNewComponent of calling the parent version)
        for (int i=0; i<num_components; i++)
        {
          new_components[i].CopyComponent(&src->components[i]);
        }
    
        components = new_components;
    
        new_data = src->new_data; // here our src does have new_data, so we will copy its value
      }
    
      void newModelFunction (int data) {new_data = data;}
      
      virtual CNewComponent * GetComponents() { return new_components; }
    
     public:
      int new_data;
      CNewComponent * new_components;
    };
    
    
    int main()
    {
      CModel *model = new CModel;
      CNewModel *newModel = new CNewModel;
      newModel->CopyModel (model);
      newModel->components[5].ComponentFunction(); // OK
      //newModel->components[3].new_variable = 100;   // Invalid because components points to the objects via a CComponent pointer, so it doesn't know that there really is a new_variable there
      //newModel->components[1].newComponentFunction(); // same here
    
      // To do what you wanted, try:
      newModel->new_components[3].new_variable = 100;   // OK because new_components points to objects via a CNewComponent pointer, so it knows about the existance of the new_variable
      newModel->new_components[1].newComponentFunction(); // same here
    
      // Or using the "GetComponents" function I added:
      newModel->GetComponents()[3].new_variable = 100;
      newModel->GetComponents()[1].newComponentFunction();
    }
    

    Thursday, January 27, 2011 3:41 PM
  • Hi,
    Scary... what if GetComponents() returns NULL ?
    You can't tell especially not when using an interface, there could be any kind of implementation behind it.

    Replace "CComponent *components;" by an instance of a new class called "CComponentlist".
    In that case your model class can forget all the boring details of list handling, and deal only with it's bussiness logic.
    Copying the list will be implemented in the copy-constructor of the list-class, which is where most of us would expect to find it.
    Use STL where possible for storage stuff.

    Programming C++ starts with structure, after setting up a good design the technical stuff will be much clearer, smaller chuncks, and simpler (- to understand  because of the right context)  )
    I suggest you start with a rough UML diagram on a whiteboard...

    Kind regards,
    Rob
    www.robtso.nl

     

     

     

     

     

    Thursday, January 27, 2011 4:22 PM
  • Scary... what if GetComponents() returns NULL ?
    You can't tell especially not when using an interface, there could be any kind of implementation behind it.

    Replace "CComponent *components;" by an instance of a new class called "CComponentlist".
    In that case your model class can forget all the boring details of list handling, and deal only with it's bussiness logic.
    Copying the list will be implemented in the copy-constructor of the list-class, which is where most of us would expect to find it.
    Use STL where possible for storage stuff.

    Programming C++ starts with structure, after setting up a good design the technical stuff will be much clearer, smaller chuncks, and simpler (- to understand  because of the right context)  )
    I suggest you start with a rough UML diagram on a whiteboard...


    Good points, we are working towards that.  Too many new concepts all at once can be overwhelming.  So I am trying to focus in the parent/child class relationships for now.
    Thursday, January 27, 2011 4:46 PM
  • As for platform, this code has to run on multiple environments, including MFC on WINxx, and as a non-GUI MATLAB mex function on WIN, MAC and Linux. The classes were originally written using three linked lists, but I recently changed them to arrays for two reasons. One, I decided I did it the hard way (it probably made sense at one time). The second is because the scenes are also being displayed in OpenGL. To speed things up I wanted to use Vertex Pointers which requires the data to be contiguous, which nixed using linked lists. Which leads to another question since someone mentioned using the STL array. If I use STL am I guaranteed that the data will be contiguous? I need this so I can use GL Vertex Pointers and Normal Pointers. I'm still working on implementing Fredrick's example, but am having trouble. I will get back, but I saw the above questions and thought I would answer them. Sorry if the formatting gets lost again. At work they block so many things that all I get is an edit box. The icons for entering HTML or source code are lost. Thanks, Jim
    Thursday, January 27, 2011 5:22 PM
  • One thing that concerns me with these virtual functions is what kind of performance penalty will there be (if any). This is a laser model and I've had to render scenes with up to ten million facets. If a plane is doing a flyby, the rendering code may get called hundreds of times. Of course, I'm not sure I have much choice.
    There is some performance penalty with virtual functions, but I would think it is only significant if the functions themselves are very simple.
     
    But only you can decide.
     
    I know nothing about gaming, but I would have thought that polymorphism would be a significant simplifying feature.
     

    David Wilkinson | Visual C++ MVP
    Thursday, January 27, 2011 5:30 PM
  • As for platform, this code has to run on multiple environments, including MFC on WINxx, and as a non-GUI MATLAB mex function on WIN, MAC and Linux. The classes were originally written using three linked lists, but I recently changed them to arrays for two reasons. One, I decided I did it the hard way (it probably made sense at one time). The second is because the scenes are also being displayed in OpenGL. To speed things up I wanted to use Vertex Pointers which requires the data to be contiguous, which nixed using linked lists. Which leads to another question since someone mentioned using the STL array. If I use STL am I guaranteed that the data will be contiguous?
    In the latest C++ standard, the elements of std::vector are guaranteed to be contiguous. In earlier versions this was not the case, but to my knowledge all known implementations of std::vector use contiguous storage.
     
    See, for example:
     
    http://groups.google.com/group/comp.lang.c++/msg/408d058c256d7699
     

    David Wilkinson | Visual C++ MVP
    • Marked as answer by JimmyG Friday, February 4, 2011 2:52 PM
    Thursday, January 27, 2011 5:37 PM
  • Just a thought to help the 'other' frustrating thing...

    ...there are other code formatters that you can use to format code, but...

    ...I copy from VS onto the clipborad...then, paste into a blank Word document, then Ctl-A, Ctl-C to put it back onto the clipboard...then Ctl-V directly into the body of the post, like this:

     

        Public Shared Function CompareObjects(ByVal objA As Object, _

                                              ByVal objB As Object) As Boolean

            Return objB Is objA

        End Function

     

     

     

    Thursday, January 27, 2011 5:47 PM
  • Another question about std::vector. I also will be sorting the facet data (contained in CComponent) into a quad tree about four levels deep. Each branch contains an index array that points into the original data array. And each sub-branch re-sorts those indexes again. If I use std::vector to store the facet data will it cause a problem with my sorting? Before I try it, I want to make sure I'm not off on a wild goose chase. Either way, I can still use std::vector or the model and component arrays. But I still have to get the previous problem fixed first...
    Thursday, January 27, 2011 6:11 PM
  • If I use std::vector to store the facet data will it cause a problem with my sorting?

    There is a function std::sort() that can sort vectors, if that is what you are asking.
    Thursday, January 27, 2011 6:27 PM
  • Actually, I realized that I asked a stupid question. Since I have access to the array I can manipulate it any way that I want. However, the sort function won't work because I am dividing it up into quandrants (since it is an quad tree) so I have to do my own sorting.
    Thursday, January 27, 2011 8:06 PM
  • Fredrick, I think there is a flaw in your sample code. In the function CopyModel you have a line that says components = new_components; This doesn’t work because this is an array of objects, but the objects are two different sizes. For example, let’s say that CComponent contains a single integer so that each entry is four bytes. Now let’s suppose that CNewComponent contains two integers so that each entry is now eight bytes. The problem with setting component equal to new_component is that when functions in the CComponent base class traverse the array they only increment the offset by four bytes instead of eight because they still think it is a CComponent class instead of a CNewComponent class. I may have to try to virtual base class next that Dave suggested. I wanted to see if I could get this method working first because it seemed easier. Thanks, Jim
    Thursday, January 27, 2011 8:46 PM
  • Hi,
    I see nothing in your original question about sorting, Do you want to sort, or is it something else you want to achieve ?
    First ask yourself WHAT is it you want to achieve (not HOW).  Donot focus (too much) on technical issues in this stage, try to postpone them...
    Adjust your model/interfaces according to the functional terms (=members) and verbs (=objects) that you encounter in your answer (be very critical here, rephrase a few times to see what fits best, this is a critical phase and might start you off in the wrong direction when done wrong). Draw it on a whiteboard, erase and start over a few times, till you get it right , there is no single solution, just choices...

    Keep in mind that your customer probably doesn't care about openGL, he needs some task performed. You the developer have chosen that openGL should be used to achieve it , right ?
    If you explain to someone what your program does, you'll probably not even mention it, let alone some internal issue about continuous memory locations and sorting.

    If some functional task happens to involve sorting, thats fine, but the complex details of that will be hidden from your bussiness logic by some (functional) membercall.
    ( like for example: "pRenderer->DrawTheModel()" )
    Renderer itself might contain other classes containing all sorts of technically complex stuff to do it's thing (like sorting some array), but at the highest level, who cares ?

    This is basic OO stuff, the guidelines are always the same, but it takes some getting used to, and a different way of thinking from functional programming. I'm not saying one is always better than the other, but I like it as a way to manage complexity ,modularity, and dependency's. (interfaces)

    Performance is not neccesarily worse, but as always, you need to know what you're doing.

    Good luck,
    Rob
    www.robtso.nl

     


     

    • Marked as answer by Jesse Jiang Wednesday, February 2, 2011 8:30 AM
    • Unmarked as answer by JimmyG Friday, February 4, 2011 2:51 PM
    Thursday, January 27, 2011 8:46 PM
  • Fredrick, I think there is a flaw in your sample code. In the function CopyModel you have a line that says components = new_components; This doesn’t work because this is an array of objects, but the objects are two different sizes. For example, let’s say that CComponent contains a single integer so that each entry is four bytes. Now let’s suppose that CNewComponent contains two integers so that each entry is now eight bytes. The problem with setting component equal to new_component is that when functions in the CComponent base class traverse the array they only increment the offset by four bytes instead of eight because they still think it is a CComponent class instead of a CNewComponent class. I may have to try to virtual base class next that Dave suggested. I wanted to see if I could get this method working first because it seemed easier. Thanks, Jim
    Yes, as I mentioned before, if you want a heterogeneous collection, the collection must hold base class pointers, not objects. In this case, CComponent* Or if you have introduced an interface IComponent, then IComponent*.
     
    Even better if you use shared_ptr<CComponent>, or shared_ptr<IComponent>, which will take care of lifetime issues.
     

    David Wilkinson | Visual C++ MVP
    • Marked as answer by Jesse Jiang Wednesday, February 2, 2011 8:30 AM
    Thursday, January 27, 2011 10:38 PM
  • Fredrick, I think there is a flaw in your sample code. In the function CopyModel you have a line that says components = new_components; This doesn’t work because this is an array of objects, but the objects are two different sizes. For example, let’s say that CComponent contains a single integer so that each entry is four bytes. Now let’s suppose that CNewComponent contains two integers so that each entry is now eight bytes. The problem with setting component equal to new_component is that when functions in the CComponent base class traverse the array they only increment the offset by four bytes instead of eight because they still think it is a CComponent class instead of a CNewComponent class. I may have to try to virtual base class next that Dave suggested. I wanted to see if I could get this method working first because it seemed easier. Thanks, Jim

    Yes you are right that code won't work.  Rob__ offers some good advice.  How much control over CModel do you have?  One problem you have is that you have two datas, CModel's components and CNewModel's new_components.  C++ lets us have virtual function overloading, but not virtual data overloading.  Some options you could try are:
    - Ever member function in CModel that interacts with components must be made virtual and overriden in CNewModel to work with the new_components.  In this manner we essentially abandon use of CModel::components when the object is actually a CNewModel.

    - Have CModel hold own the components, but make it do so by pointer so that the actual components objects could actually be CComponents or CNewComponents.

    - Whenever CModel access's the components, have it do so through a virtual function.  This way the virtual function can properly get CModel::components when the object is CModel and get CNewModel::new_components when the object is a CNewModel.

    • Marked as answer by Jesse Jiang Wednesday, February 2, 2011 8:30 AM
    Thursday, January 27, 2011 11:35 PM
  • I have total control over Cmodel since I wrote it.  I’m starting to think that Fredrick’s second option, which is to have Cmodel contain an array off CComponent pointers rather than just Ccomponent’s makes the most sense.  This is essentially what Dave suggested above. 

    But I started thinking maybe I’m taking the wrong approach in the first place, so maybe I need to step back.

    In my original working version I had one CModel and CComponent class and everything was self-contained.  I recently made a major change and in the process I decided it be more object-oriented to create a base class and then extend it to add functionality.  But I didn’t realize how difficult it would be.

    The only difference between CComponent and CNewComponent is that I added a class called CQuadTree, like this:

    Class CNewComponent : CComponent public
    {
      CQuadTree *quadtree;
    };

    Otherwise, they are identical.  The quad tree just takes the facet data and sorts it and traverses the tree when I render the data.  The base classes are simply for holding and displaying the data.

    So I could just put the quad_tree back in the base class and solve the problem. 

    But now I am wondering if either approach is the best approach, since the quad_tree just acts on the data.  Should the CComponent contain a quad_tree, or should the quad_tree contain the data (or neither)?  I could do something like (assume the model and it’s components are already defined):

    CModel *model;
    CQuadTree *quad_tree;
    
    quad_tree = new CQuadTree [num_components];
    
    for (i=0; i<num_components; i++)
      quad_tree[i].CreateTree (&model->components[i]);
    
    // And to use it:
    
    for (i=0; i<num_components; i++)
      quad_tree[i].Render();
    

    The only drawback is that the quad_tree has to depend on the data being in CComponent because it is sorting the data using an index array.  If the model should get deleted for some reason (which shouldn’t happen unless I screw up), then it will be acting on garbage.
       
    So I’m not sure now what makes the most sense:

    1. Stick the quad tree back in the base class and be done with it.
    2. Use the array of component pointers as just suggested
    3. Do what I just mentioned with the array of quad_tree’s
    4. Maybe none of the above because there is a better approach if anyone has an idea.

    Thanks again.

    • Marked as answer by Jesse Jiang Wednesday, February 2, 2011 8:30 AM
    Friday, January 28, 2011 1:49 AM