2011-08-25 80 views
12

对不起,我确实想要具体。 我期待下面的代码工作,但它并没有和我想不通为什么:/如何将一个指向派生类的指针传递给期望引用基类指针的函数?

#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->GetBuildingType(); 
    SafeDispose(b);  // error C2664: 'SafeDispose' : cannot convert parameter 1 from 'Building *' to 'UniquePointer *&' 
    b->Dispose(); 
} 

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

请注意,您正在尝试做的事情没有任何“安全”。如果'b'设置为'NULL',那么'b-> Dispose();'会导致行为,就像'b'指向一个被删除的对象一样。 –

+0

@Charles:编辑,等待审查。这个bug不是问题的一部分,是吗? –

+0

此外,由于'Dispose'是公开的,因此不需要'SafeDispose'成为朋友。 –

回答

42

想象它是合法的。然后,你可以这样写代码:

​​

注意你已经违反了类型系统(你把一个动物如建筑物应该是),而无需浇注或者提高编译错误。

+0

我明白你的观点。我想要引用指针的原因是能够将该指针设置为NULL,但我显然错过了所有可能产生的副作用。 –

3

这是不允许的,因为如果它是你能做到以下几点:

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


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

我想周围的工作将是一个模板函数。

+0

除了你不能实例化唯一的指针,它必须被子类化。但我明白你的观点。 –

6

我不认为有可能使它按照您设计的方式工作。相反,尝试以下操作:

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() { } 
}; 
+0

我喜欢你的解决方案,实际上你应该告诉我为什么不能像我这样做,这是我接受的答案。 –

0

我猜你SafeDispose应该看起来可能更像:

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

为了调用它使用

SafeDispose(&(UniquePointer*)b); 

那么就应该以这种方式工作。

但你的下一个语句

b->Dispose(); 

将打破事业B现在应该是NULL,因为它已经被你的SafeDispose方法布置并设置为NULL。

+0

但是'&b'是'Building **'类型的右值,你不能传递给一个带有'UniquePointer **'的函数,这是因为你不能绑定'Building * -const'UniquePointer *&'。 –

+0

感谢您指出这一点。添加演员阵容可能会解决这个问题吗?! (更新示例代码)。 然后我猜wilx答案(通过使用模板)更有意义。 –

+1

不,铸造不能解决它创建新的问题。转换转换指针,但转换的结果是一个右值,然后你不能取右值的地址。 –

1

要回答您的问题的标题,您不能将基于非常量的引用绑定到派生类实例,因为您可以将该引用设置为指向不是派生的基本实例的指针。考虑一下这个功能:

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

如果你能传递一个指针Building你就可以将其设置错误地指向一个UniquePointer实例。

由于已经提出解决方案是将您的引用更改为普通指针。这不仅解决了你的问题,而且也是更好的实现SafeDispose();正如你写的那样,这个函数给出了一个错误的想法,即你总是将所有的UniquePointer实例设置为0。但是,如果有人写了,会发生什么(假设UniquePointer构造是公开为简单起见):

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

他们期望所有的UniquePointer S的要妥善保管,当p2实际上是无效的。

+0

我可能已经给了一切比我做的更好的名字。我希望SafeDispose将指针设置为NULL并检查NULL,但我没有考虑被调用者可能拥有指针副本这一事实。但重要的是要注意,没有任何共享,我在阅读Google的编码风格后对“单一所有权”系统感兴趣。 –

相关问题