References and implicit casts

Started by
9 comments, last by TheAdmiral 16 years, 5 months ago
Hi! I have a class Foo and a bunch of other classes just like Foo that implement an interface IBar like so:

class IBar
{
public:
  virtual void DoBar() = 0;
};

class Foo : public IBar
{
public:
  void DoBar
  {
    ...
  }
};
I also have another interface IFooBar

class IFooBar
{
public:
  virtual IBar GetBar() = 0;
};
and a couple of classes that implement IFooBar. However, I want each of these classes to return their own implementation of IBar in their GetBar-method:

IBar MyFooBar::GetBar()
{
  return Foo();
}
Now I'm not sure if that's even legal (I doubt it), but it gets worse:

MyFooBar y;
IBar& x = y.GetBar();
This gives me the following error "IBar: cannot instantiate abstract class". So what's my best bet here? When I return by reference it seems to work, but I'm not sure if that's the best and/or nicest way of doing it. Since the GetBar method returns a new object, returning by reference doesn't seem like a good idea. However, I also don't want to sacrifice my interfaces... Thanks a lot in advance!
Advertisement
To return objects of different types, you need to return a reference or a pointer. Seeing as you wish to return a new object, you cannot return a reference. So, make GetBar return an IBar pointer or a smart IBar pointer, such as std::auto_ptr<IBar> or boost::shared_ptr<IBar>.
Thanks for the reply. Very helpful!
Since the problem's resolved and we're on the topic of implicit casts and references, perhaps somebody could fill a gap in my C++ knowledge.

Many people use the SAFE_RELEASE macro to release COM objects without having to worry about dereferencing a null pointer. Like many, I object exclusively to the use of macros, but I came a-cropper when trying to implement an equivalent function:
void SafeRelease(IUnknown* pRes) {    if (pRes != NULL) pRes->Release();}
This works just fine, but ideally I'd have the pointer nullified after releasing its object. So as not to have to change client code, I figured a reference-to-pointer would be the solution, but the compiler complains about implicit casting-to-reference when I try this:
void SafeRelease(IUnknown*& pRes) {    if (pRes != NULL) pRes->Release();    pRes = NULL;}
Is there any hope for my plight?

Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
Quote:
the compiler complains about implicit casting-to-reference when I try this


What error message do you get? Can you post a minimal example which generates this error?
Quote:Original post by rip-off
What error message do you get? Can you post a minimal example which generates this error?
Sorry - I should have bothered to try to compile this before posting. The situation isn't as simple as I made out. Everything compiles fine when I pass it a IUnknown*, but the problem is that it doesn't (or I don't) know how to down-cast a pointer to a derived class appropriately:


void SafeRelease(IUnknown*& pRes) {    if (pRes != NULL) pRes->Release();    pRes = NULL;}class Derived : public IUnknown {};void ProblemFunction() {    Derived* instance; // Let's pretend this has been initialised    SafeRelease(instance);}


This gives a compile error:

error C2664: 'SafeRelease' : cannot convert parameter 1 from 'Derived *' to 'IUnknown *&'

I suspect this could be solved by casting inside the call to SafeRelease, but I'd really rather not clutter up the client code.

Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
The first thing that comes to mind would be to make a template function:
template< class Derived >void SafeRelease( Derived *& pointer ){    IUnknown *unknown = pointer;    if( unknown )    {        unknown->Release();    }    pointer = 0;}


I think I can see how this could cause an error. Consider if the following example were to compile:
struct IUnknown{    virtual void Release() = 0;};struct Concrete : IUnknown{    virtual void Release(){}};struct Bad : IUnknown{    virtual void Release(){}};void bad( IUnknown *& unknown ){    unknown = new Bad;}int main(){    Concrete *concrete = new Concrete;    bad(concrete);    // Concrete pointer points to unrelated class!}
Okay, I see the problem now.

Unfortunately, DirectX offers a cornucopia of IUnknown-derived interfaces and it seems devious to specialise a template for each one I use. I think I'll just stick to clearing the pointer in client code.

Nevertheless, it's reassuring to know that haven't just missed something obvious.

Thanks for the help
Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.2

Passing a reference to a pointer is the same as passing in a pointer to a pointer in this case, except the semantics are different.

The same problem exists; because the IUnknown pointer is modifyable, you could make it point to something completely random that's derived from IUnknown.

For example, if your function was legal, I could do this:
void SafeRelease(IUnknown*& pRes) {    ID3DDevice9* D3DDevice;    // ...    pRes = D3DDevice;}

And it would work fine. And what if I did this with your function:
IDirectInput8* Input;// ...SafeRelease(Input);


My IDirectInput8* would be made to point to an ID3DDevice9. Which would be very, very bad.
NextWar: The Quest for Earth available now for Windows Phone 7.
Quote:Original post by TheAdmiral
Unfortunately, DirectX offers a cornucopia of IUnknown-derived interfaces and it seems devious to specialise a template for each one I use. I think I'll just stick to clearing the pointer in client code.


Why not use the template? It will work for every IUnknown derived interfaces, without specialisation. You shouldn't have to specialise it. Try it out.

This topic is closed to new replies.

Advertisement