开发者

How come a pointer to a derived class cannot be passed to a function expecting a reference to a pointer to the base class?

开发者 https://www.devze.com 2023-03-30 08:35 出处:网络
Sorry for the long title but I did want to be specific. I expected the following code to work but it doesn\'t and I can\'t figure out why :/

Sorry for the long title but I did want to be specific. I expected the following code to work but it doesn't and I can't figure out why :/

#include <cstdio>
#include <cassert>

class UniquePointer
{
public:
    void Dispose()
    {
        delete this;
    }

    friend void SafeDispose(UniquePointer*& p)
    {
        if (p != NULL)
        {
            p->Dispose();
            p = NULL;
        }
    }
protected:
    UniquePointer() { }
    UniquePointer(const UniquePointer&) { }
    virtual ~UniquePointer() { }
};

class Building : public UniquePointer
{
public:
    Building()
    : mType(0)
    {}
    void SetBuildingType(int type) { mType = type; }
    int GetBuildingType() const { return mType; }
protected:
    virtual ~Building() { }
    int mType;
};

void Foo()
{
    Building* b = new Building();
    b->SetBuildingType(5);
    int a = b->GetBuildingT开发者_运维百科ype();
    SafeDispose(b);     // error C2664: 'SafeDispose' : cannot convert parameter 1 from 'Building *' to 'UniquePointer *&'
    b->Dispose();
}

int main(int argc, char* argv[])
{
    Foo();
    return 0;
}


Imagine it were legal. Then you could write code like this:

class Animal : public UniquePointer
{
};

void Transmogrify(UniquePointer*& p)
{
    p = new Animal();
}

void Foo()
{
    Building* b = nullptr;
    Transmogrify(b);
    b->SetBuildingType(0); // crash
}

Observe that you have violated the type system (you put an Animal where a Building should be) without requiring a cast or raising a compiler error.


I do not think that it is possible to make it work the way you have it designed. Instead, try the following:

template <typename T>
void SafeDispose(T * & p)
{
    if (p != NULL)
    {
        p->Dispose();
        p = NULL;
    }
}

class UniquePointer
{
public:
    void Dispose()
    {
        delete this;
    }

protected:
    UniquePointer() { }
    UniquePointer(const UniquePointer&) { }
    virtual ~UniquePointer() { }
};


It is not allowed because if it were you could do the following:

friend void SafeDispose(UniquePointer*& p)
{
    p = new UniquePointer();   
}


Building* building;
SafeDispose(building)
//building points to a UniquePointer not a Building.

I guess the work around would be a template function.


To answer the title of your question, you cannot bind a non-const reference to base to a derived class instance because you could then set that reference to a pointer to a base instance that isn't a derived. Consider this function:

void Renew(UniquePointer *& p) {
  delete p;
  p = new UniquePointer();
}

if you could pass it a pointer to Building you would be able to set it incorrectly to point to a UniquePointer instance.

As it has already been suggested the solution is to change your reference to a plain pointer. Not only this solves your problem, but it is also a better implementation of SafeDispose(); as you wrote it this function gave the false idea that you would always set to 0 all your UniquePointer instances. But what would happen if somebody wrote (assuming UniquePointer constructor was public for simplicity):

UniquePointer *p1 = new UniquePointer();
UniquePointer *p2 = p1;
SafeDispose(p1);

They would expect all of their UniquePointers to be properly taken care of, when p2 is actually invalid.


I guess your SafeDispose should probably look more like :

friend void SafeDispose(UniquePointer** p) ...

In order to invoke it using

SafeDispose(&(UniquePointer*)b);

Then it should work this way.

But your next statement

b->Dispose(); 

will break cause b should now be NULL, cause it has been disposed and set to NULL by your SafeDispose method.

0

精彩评论

暂无评论...
验证码 换一张
取 消