CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 16
  1. #1
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Reference to a pointer of another type

    The minimum amount of code that reproduces the problem. I have a pointer of static type A, which points to a valid B.

    I'd like to handle said pointer like a B, but not create a new pointer => I though of using a reference.

    Code:
    class A{};
    class B : public A{};
    
    int main()
    {
      A* pA = new B;
      B*& pB1 = static_cast<B*>(pA); //error: invalid initialization of non-const reference of type 'B*&' from a temporary of type 'B*'
      B*& pB2 = static_cast<B*&>(pA); //error: invalid static_cast from type 'A*' to type 'B*&'
    }
    I can understand why both of these examples don't compile, but how could I get it to work? I pretty sure what I'm trying to do is legal...

    I'd use reinterpret_cast, but it wouldn't work if B actually had multiple inheritance...

    Any help?
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  2. #2
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: Reference to a pointer of another type

    I'm curious, what's the problem with creating a new pointer?

  3. #3
    Join Date
    Jun 2008
    Posts
    592

    Re: Reference to a pointer of another type

    I am confused about what you mean, but how about
    Code:
    class A{};
    class B : public A{};
    
    int main()
    {
      A* pA = new B;
      B& pB1 = static_cast<B&>(*pA);
    }
    and with *& as an example
    Code:
    class A{};
    class B : public A{};
    
    int main()
    {
      A* pA = new B;
      B* pB1 = static_cast<B*>(pA); 
      B*& pB2 = pB1; 
    }
    0100 0111 0110 1111 0110 0100 0010 0000 0110 1001 0111 0011 0010 0000 0110 0110 0110 1111 0111 0010
    0110 0101 0111 0110 0110 0101 0111 0010 0010 0001 0010 0001 0000 0000 0000 0000
    0000 0000 0000 0000

  4. #4
    Join Date
    Oct 2008
    Posts
    1,456

    Re: Reference to a pointer of another type

    why not "B* const & pB1 = static_cast<B*>(pA);" which is also more correct, because changing the pointer through that reference seems dangerous ... ( note that the temporary is bound to the const reference in this case )

    I pretty sure what I'm trying to do is legal...
    in addition to make sure that pA always points to a valid B, you should also make sure that it always points to the right B, as the result of the static cast may change by varying B's dynamic type ...

    EDIT: thinking about it, I also echo Lindley's question
    Last edited by superbonzo; December 9th, 2011 at 11:48 AM.

  5. #5
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: Reference to a pointer of another type

    [double post]
    Last edited by monarch_dodra; December 9th, 2011 at 12:10 PM.
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  6. #6
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: Reference to a pointer of another type

    The scenario is this:

    We have class Worker, and we have class Manager. Each Manager manages exactly 1 Worker.

    Because of this, basically, the Manager Class has a Worker* member for manipulating the corresponding worker in a generic fashion.

    here is the catch. There are 3 types of worker: worker1, worker2 and worker3. These workers have methods that are unique to them (eg not shareable via the base interface). To manage these workers, we have Manager1, Manager2 and Manager3 etc...

    Manager1 only handles Worker1 etc...

    Basically, it is parallel hierarchy.

    So basically:

    Code:
    class Manager
    {
    protected:
      Worker* pWorker;
    };
    
    class Manager1 : public Manager
    {
    public:
      Manager1() : Manager()
      {pWorker = new Worker1;}
    }
    Inside Manager, we put all the code that is generic, and inside Manager1, we put the code that is specific to the work logic of 1.

    While we are inside Manager1, we are guaranteed that pWorker is pointing to a valid pWorker1.

    Because we have a lot of code, we didn't want to wrap static casts each and every time we used pWorker, so we tried binding a Worker1 reference to it, to use it seemlessly, basically, like this:

    Code:
    class Manager
    {
    public:
      virtual void some_method() = 0;
    
    protected:
      Worker* pWorker;
    };
    
    class Manager1 : public Manager
    {
    public:
      Manager1() : Manager()
      , pWorker ( reinterpret_cast<Worker1*>(Manager::pWorker) )
      {pWorker = new Worker1;}
    
      void some_method()
      {
        pWorker->some_worker1_specific_method(); //cool, this works!
      }
    
    protected:
      Worker1*& pWorker; //NAME SHADOWING HERE
    }
    With this setup, we can basically use the parent's "pWorker", but handle it statically as a Worker1 type.

    Our code gets shuffled around (read copy pasted), and this approach makes it work seemlessly. IE: when we move some code back or forth from base to derived, there is no need to touch anything if no worker1 specific data is touched...

    I can confirm this works, but I'm trying to do it without reinterpret, in case we ever see multiple inheritance... And just to be clean
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  7. #7
    Join Date
    Jan 2009
    Posts
    596

    Re: Reference to a pointer of another type

    Quote Originally Posted by monarch_dodra View Post
    The scenario is this:

    We have class Worker, and we have class Manager. Each Manager manages exactly 1 Worker.

    Because of this, basically, the Manager Class has a Worker* member for manipulating the corresponding worker in a generic fashion.

    here is the catch. There are 3 types of worker: worker1, worker2 and worker3. These workers have methods that are unique to them (eg not shareable via the base interface). To manage these workers, we have Manager1, Manager2 and Manager3 etc...

    Manager1 only handles Worker1 etc...

    Basically, it is parallel hierarchy.
    Instead of having the pWorker in the Manager class, as a base class pointer, why not have derived class pointers in each of the Manager subclasses? You can then access these in Manager through a virtual method GetWorker():
    Code:
    class Manager
    {
    	protected:
    		virtual Worker* GetWorker() = 0;
    };
    
    class Manager1 : public Manager
    {
    	protected:
    		Worker1* pWorker1;
    
    		virtual Worker* GetWorker()
    		{ return pWorker1; };
    
    	public:
    		Manager1() : Manager()
    		{pWorker1 = new Worker1;}
    }
    In the specific Manager classes you will always have the worker as the full type, so can use this directly.
    Last edited by Peter_B; December 9th, 2011 at 12:38 PM. Reason: Forget * on Worker1 data member

  8. #8
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: Reference to a pointer of another type

    An accessor function might be a better way to go than a reference anyway, even if you keep the pointers as they are.

  9. #9
    Join Date
    Oct 2008
    Posts
    1,456

    Re: Reference to a pointer of another type

    indeed, using an accessor you don't even need a pointer by using covariant return, something like:

    Code:
    class Manager
    {
    	protected:
    		Manager( Worker* w ): worker_(w) { }
    		virtual Worker* GetWorker() { return worker_; }
    
    	private:
    		Worker* worker_;
    };
    
    class Manager1 : public Manager
    {
    	protected:
    		virtual Worker1* GetWorker() { return static_cast<Worker1*>( Manager::GetWorker() ); };
    
    	public:
    		Manager1(): Manager( new Worker1 ) {}
    }
    BTW, my suggestion in post #4 won't work with the use case in post #6, as the temporary would not survive the constructor scope in that specific case...

  10. #10
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Thumbs up Re: Reference to a pointer of another type

    Quote Originally Posted by Peter_B View Post
    Instead of having the pWorker in the Manager class, as a base class pointer, why not have derived class pointers in each of the Manager subclasses? You can then access these in Manager through a virtual method GetWorker():

    [...]
    I really do like that virtual accessor approach! I happen to have a quite similar scenario in the very code Im working on right now. Up to now I had solved the issue discussed here by physically shadowing the pointer: Manager had a Worker * member and Manager1 a Worker1 * member, both being initialized to the same value upon Manager1 construction. Now, after having changed that to use a virtual accessor, it feels way more elegant and robust!

    This thread came right on time...
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  11. #11
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: Reference to a pointer of another type

    Hii everyone, thanks for your input. I'll answer in two parts:

    --------
    1. From a strict technical point of view, I realize what I'm trying to do is illegal if the base and derive instance don't have the same address (due to offset). Indeed, how could I have a reference with a different value than what is being referenced?

    --------
    2. From an architecture point of view, we had considered using an accessor. Although to be frank, I don't see why we'd use run-time co-variant overrides, when we can "settle" for a statically resolved plain old overloads. That, and covariance doesn't really cast the pointer at compile time anyways, so it wouldn't compile according to my use case in #6 (I believe).

    I think our final solution is going to be what I showed (the name shadowing pointer approach), except instead of being "fancy" and using a reference to a pointer, I (we) 'll keep it to being a simple pointer:

    Code:
    class Manager
    {
    public:
      virtual void some_method() = 0;
    
    protected:
      Worker* pWorker;
    };
    
    class Manager1 : public Manager
    {
    public:
      Manager1() : Manager()
      , pWorker (NULL)
      {
         pWorker = new Worker1;
         Manager::pWorker = static_cast<Worker*>(pWorker);
      }
    
      void some_method()
      {
        pWorker->some_worker1_specific_method(); //cool, this works!
      }
    
    protected:
      Worker1* pWorker; //NAME SHADOWING HERE
    }
    While some could argue that keeping 2 pointers is not as clean as using the accessors (myself included), it is also the approach that has the least impact in our codebase....
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  12. #12
    Join Date
    Oct 2008
    Posts
    1,456

    Re: Reference to a pointer of another type

    Quote Originally Posted by monarch_dodra View Post
    I don't see why we'd use run-time co-variant overrides, when we can "settle" for a statically resolved plain old overloads. That, and covariance doesn't really cast the pointer at compile time anyways, so it wouldn't compile according to my use case in #6 (I believe).
    you are speaking of my code in post #9, aren't you ? if yes, AFAIK there's no "runtime cast" involved here. There are two static casts ( one in the Manager1::GetWorker body and the other, implicit, in the compiler generated virtual dispatch of Manager::GetWorker ). So, the only potential overhead I see is in the virtual call.
    Moreover, you have the benefit of a safe behavior when MI is involved ( with respect to a reference based solution ), and you spare a pointer for each subclass in the hierarchy.

    anyway, I got your point ...

  13. #13
    Join Date
    May 2009
    Posts
    2,413

    Re: Reference to a pointer of another type

    Quote Originally Posted by monarch_dodra View Post
    While some could argue that keeping 2 pointers is not as clean as using the accessors (myself included), it is also the approach that has the least impact in our codebase....
    I always try to keep base classes pure interfaces but say this is an exception. Then at least I would go for a cleaner and safer implementation, like
    Code:
    class Worker {
    };
    
    class Manager
    {
    public:
      virtual void some_method() = 0;
    
    protected:
      explicit Manager(Worker* p) : pWorker(p) {}
    
    private:
      Worker* pWorker;
    };
    
    class Worker1 : public Worker {
    public:
      void some_worker1_specific_method() {
    	  std::cout << "hello\n";
      }
    };
    
    class Manager1 : public Manager
    {
    public:
      Manager1() : pWorker(new Worker1), Manager(pWorker) {}
    
    private:
    
      void some_method()
      {
        pWorker->some_worker1_specific_method();
      }
    
      Worker1* pWorker;
    };
    
    // test
    
    Manager* m = new Manager1;
    m->some_method();
    Last edited by nuzzle; December 10th, 2011 at 11:29 AM.

  14. #14
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: Reference to a pointer of another type

    Quote Originally Posted by nuzzle View Post
    Then at least I would go for a cleaner and safer implementation, like
    Hi nuzzle, thanks for your reply. I liked your protected constructor approach.

    However, I do believe the code you provided produces undefined behaviour:

    Code:
    Manager1() : pWorker(new Worker1), Manager(pWorker) {}
    indeed, construction order is defined by order of declaration in the class definition, and NOT the order in the mem-init-list. eg: Manager(pWorker) will be executed first, point at which pWorker will only be garbage...

    This should be roughly equivalent:

    Code:
    Manager1() : Manager(new Worker1), pWorker(static_cast<Worker1*>(Manager::pWorker)) {}
    But it requires Manager::pWorker to be only protected though.
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  15. #15
    Join Date
    May 2009
    Posts
    2,413

    Re: Reference to a pointer of another type

    Quote Originally Posted by monarch_dodra View Post
    indeed, construction order is defined by order of declaration in the class definition, and NOT the order in the mem-init-list. eg: Manager(pWorker) will be executed first, point at which pWorker will only be garbage...
    Well, so much for safety . In fact I had that nagging feeling it wasn't kosher but instead of looking in the standard I checked that Manager had a valid pWorker pointer. And it was so I let it stand.

    But okay, then to get rid of the ugly downcast I would prefer this,

    Code:
    class Worker {
    };
    
    class Manager
    {
    public:
      virtual void some_method() = 0;
    
    protected:
      Manager() : pWorker(nullptr) {}
    
      Worker* pWorker;
    };
    
    class Worker1 : public Worker {
    public:
      void some_worker1_specific_method() {
        std::cout << "hello\n";
      }
    };
    
    class Manager1 : public Manager
    {
    public:
      Manager1() : pWorker(new Worker1) {
        Manager::pWorker = pWorker;
      }
     
    private:
      void some_method()
      {
        pWorker->some_worker1_specific_method(); //cool, this works!
      }
    	
      Worker1* pWorker;
    };
    It's also a good idea I think to put in a getter/setter pair to pWorker in Manager. The setter would be protected and the getter private. Then one could assert that pWorker is set once and once only, and that it has been set when it's used. In release mode the assertion will be gone and the getter/setter inlined so there's no performance hit, just increased safety. And since all pWorker accesses in Manager now goes via a getter you can later easily change to the suggested virtual getter approach (which is the standard OO solution in this case).
    Last edited by nuzzle; December 11th, 2011 at 01:09 AM.

Page 1 of 2 12 LastLast

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