Thread: Dynamic class variable

  1. #1
    Registered User
    Join Date
    Jan 2008
    Posts
    24

    Dynamic class variable

    I have a base class, A, with an inquiry method bool isB(), and two derived classes, B and C, that inherit from A. I also have a container class, D, with a variable, x, and its respective accessor, x(). My problem, is that x needs to be a pointer to type B --or-- C, so that when I do something like D *test = new D();, then test->x() will either be a pointer to B or a pointer to C, which I won't know until run time and could then check using the inherited method, test->x()->isB(), which will be set within B and C's constructors to true or false, respectively.

    So, my question is, what should the data type for x and return type for x() be? I was thinking like void *, but wasn't sure if that's correct or not.
    Last edited by drrcknlsn; 01-18-2008 at 01:35 PM.

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I have a base class, A, with an inquiry method bool isB(), and two derived classes, B and C, that inherit from A.
    Is isB() really necessary? It pretty much means that the base class must know about one of its child classes. If you can get away without it, that would be better.

    So, my question is, what should the data type for x and return type for x() be? I was thinking like void *, but wasn't sure if that's correct or not.
    It should be an A*.
    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

  3. #3
    Registered User
    Join Date
    Jan 2008
    Posts
    24
    Quote Originally Posted by laserlight View Post
    Is isB() really necessary? It pretty much means that the base class must know about one of its child classes. If you can get away without it, that would be better.
    The only reason isB() exists is so D objects will know whether x() points to a B or C. What would be a better way to test this?

    Quote Originally Posted by laserlight View Post
    It should be an A*.
    A pointer to a base class can also point to any derived classes as well without breaking things? If I'm understanding you correctly, and use A* as the data type for D::x and return type for D::x(), then the following should be possible, right?

    Code:
    B *b = new B();
    C *c = new C();
    D *d = new D();
    
    //  point x to b
    d->x ( b );
    
    cout << d->x->some_class_B_method () << endl;
    
    //  point x to c
    d->x ( c );
    
    cout << d->x->some_class_C_method () << endl;

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    The only reason isB() exists is so D objects will know whether x() points to a B or C. What would be a better way to test this?
    It would be even better if you can design it such that D objects do not need to know if x really is a B or a C, but only needs to know it is an A. One way to do this is by the appropriate use of virtual functions.

    A pointer to a base class can also point to any derived classes as well without breaking things?
    Of course. This is one way to use polymorphism in C++.

    If I'm understanding you correctly, and use A* as the data type for D::x and return type for D::x(), then the following should be possible, right?
    I am assuming that D::x() takes and returns an A*. If so, so, what you wrote will not work. With an A*, you can only call member functions from A, not member functions specific to its derived classes, unless you type cast (in which case you need to be sure that the object really is of the type that you cast it to).
    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

  5. #5
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by drrcknlsn View Post
    The only reason isB() exists is so D objects will know whether x() points to a B or C. What would be a better way to test this?
    You can use dynamic_cast, but there is a slight performance cost using that.
    Instead of isB() you can have something like ClassType() which returns an enum value specifying the class type.

    Quote Originally Posted by drrcknlsn View Post
    A pointer to a base class can also point to any derived classes as well without breaking things? If I'm understanding you correctly, and use A* as the data type for D::x and return type for D::x(), then the following should be possible, right?

    Code:
    B *b = new B();
    C *c = new C();
    D *d = new D();
    
    //  point x to b
    d->x ( b );
    
    cout << d->x->some_class_B_method () << endl;
    
    //  point x to c
    d->x ( c );
    
    cout << d->x->some_class_C_method () << endl;
    No, if x is an A* pointer then it can only access A functions.

  6. #6
    Registered User
    Join Date
    Jan 2008
    Posts
    24
    Right. I didn't think it would work like that, which is why I was asking about void *.

    The only other option I can think of that satisfies my requirements is splitting D::x into two different variables, with accessors B *D::x_B() and C *D::x_C(). I didn't really want to do this, because it seems like bad design to have to test for a null pointer every time I want to use x.

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    The only other option I can think of that satisfies my requirements is splitting D::x into two different variables, with accessors B *D::x_B() and C *D::x_C(). I didn't really want to do this, because it seems like bad design to have to test for a null pointer every time I want to use x.
    Why not share with us an overview of the real problem you are trying to solve? Perhaps someone here can suggest an appropriate solution, e.g., using a design pattern or something.
    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

  8. #8
    Registered User
    Join Date
    Jan 2008
    Posts
    24
    Well, basically...

    A = student
    B = undergraduate (inherits student)
    C = graduate (inherits student)
    D = class
    D::x = class role (list of all students in the class)

    So, to send a message to all students in a class, I would have liked to have D::x be a pointer to a linked list of "mixed" types (both undergrads and/or grads) that I could iterate through.

    Instead, it appears that I'll have to have D::x be a list of undergrads only, and create a new D::y for grads, and then test both for null pointers and iterate them separately.



    If anyone has a better design, I'm very much open to suggestions!

  9. #9
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    So, to send a message to all students in a class, I would have liked to have D::x be a pointer to a linked list of "mixed" types (both undergrads and/or grads) that I could iterate through.
    That sounds like you want a std::list<Student*>, or perhaps more appropriately a std::vector<Student*> or Boost's ptr_vector<Student>.

    Instead, it appears that I'll have to have D::x be a list of undergrads only, and create a new D::y for grads, and then test both for null pointers and iterate them separately.
    So far the only reason why you "have to have" it differently is that you want to call member functions specific to Undergraduate and Graduate. What are these member functions? Perhaps they can be eliminated by providing one or more virtual functions in Student.
    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

  10. #10
    Registered User
    Join Date
    Jan 2008
    Posts
    24
    Quote Originally Posted by laserlight View Post
    So far the only reason why you "have to have" it differently is that you want to call member functions specific to Undergraduate and Graduate. What are these member functions? Perhaps they can be eliminated by providing one or more virtual functions in Student.
    Correct - things like uint16 Graduate::GRE(), which returns a graduate student's GRE score. While this probably isn't the best example, since all students can take the GRE regardless of undergrad/grad status, in my implementation, I only care about the score if the student is a grad, and it didn't make sense to add such methods to the base class if they would only ever be called on objects of the Graduate class.

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    There are (more than) two ways you can solve this type of problem. In this case, if you want a particular type of student "have or not have" a particular capability, why not make it a function that returns a bool to say if it's there or not?

    Something like this:
    Code:
    class student
    {
       public:
          virtual string studenType() { return string("Any student"); };
          virtual bool GRE(int &grade)  { grade = 0; return false; };
       ...
    };
    
    class graduate : public student
    {
       public:
          virtual string studenType() { return string("Graduate student"); };
          virtual bool GRE(int &grade)  { grade = gre; return true; };
       private:
          int gre;
    };
    You can then do something like:
    Code:
       D *d;
       student *s;
       int gre;
    ...
       s = d->x();
       if (s->GRE(gre)) { cout << "GRE: " << gre << endl;
    }
    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    It should be noted that semicolons after inline class member functions are not required:
    Code:
    class student
    {
       public:
          virtual string studenType() { return string("Any student"); };
          virtual bool GRE(int &grade)  { grade = 0; return false; };
       ...
    };
    
    class graduate : public student
    {
       public:
          virtual string studenType() { return string("Graduate student"); };
          virtual bool GRE(int &grade)  { grade = gre; return true; };
       private:
          int gre;
    };
    You do, of course, need them for "prototypes":
    Code:
    class test {
        void required();
        void non_required() {}
    };
    
    void test::required() {}
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by dwks View Post
    It should be noted that semicolons after inline class member functions are not required:
    Code:
    class student
    {
       public:
          virtual string studenType() { return string("Any student"); };
          virtual bool GRE(int &grade)  { grade = 0; return false; };
       ...
    };
    
    class graduate : public student
    {
       public:
          virtual string studenType() { return string("Graduate student"); };
          virtual bool GRE(int &grade)  { grade = gre; return true; };
       private:
          int gre;
    };
    You do, of course, need them for "prototypes":
    Code:
    class test {
        void required();
        void non_required() {}
    };
    
    void test::required() {}
    Thanks for that clarification. Most of my class member functions are defined "outside" the class when I work on classes at work, so I'm not so used to writing inline functions...

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    Registered User
    Join Date
    Jan 2008
    Posts
    24
    Quote Originally Posted by matsp View Post
    Something like this:
    Code:
    class student
    {
       public:
          virtual string studenType() { return string("Any student"); };
          virtual bool GRE(int &grade)  { grade = 0; return false; };
       ...
    };
    
    class graduate : public student
    {
       public:
          virtual string studenType() { return string("Graduate student"); };
          virtual bool GRE(int &grade)  { grade = gre; return true; };
       private:
          int gre;
    };
    Student is supposed to be an abstract class that will never be instanced, but I get what you're saying.

    Would it not be more efficient to have a dedicated inquiry method (like virtual bool Student::isGraduate() = 0; - what I was alluding to in the OP) rather than returning the answer through every single method? Then, you could block the code after a test, rather than testing each individual method call.

  15. #15
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    The point is that generally speaking, you should be using public inheritance to model an is-a or works-like-a relationship, and if you have a collection of pointers to the base class then you should probably only be performing operations that makes sense for that base class.

    One possible solution that will allow you to keep your current design but avoid specialized code for different derived classes is to look at the code that you think needs to call the isGraduate() function. Is that code performing a more general duty that should be handled by the Student class?

    For example, let's say you need the GRE score only for graduates in code that outputs the transcript because on graduate transcripts there is an extra line for the GRE score. Then perhaps outputTranscript should be a method of Student than is at least partially implemented by virtual methods that are overridden in the derived classes. Then the graduate class can output the GRE score as necessary while the undergraduate class will not.

    So go up a level and look at the purpose of the code where you think you need the isGraduate() functionality and see if maybe it makes more sense to implement it as part of the Student class instead.

    If that code relies on some other object or inheritance hierarchy, then you can get them to work together with a specific design pattern (the name of which escapes me at the moment).

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need some help...
    By darkconvoy in forum C Programming
    Replies: 32
    Last Post: 04-29-2008, 03:33 PM
  2. global and static variable in a class delivered in a DLL
    By George2 in forum C++ Programming
    Replies: 16
    Last Post: 04-13-2008, 08:19 AM
  3. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  4. dynamic array of base class pointers
    By Corrington_j in forum C++ Programming
    Replies: 1
    Last Post: 11-16-2003, 05:58 AM
  5. Warnings, warnings, warnings?
    By spentdome in forum C Programming
    Replies: 25
    Last Post: 05-27-2002, 06:49 PM