6

我试图通过传递对指针的引用而不是指向指针的指针来回答这里提到的问题,如下所示:

class Parent 
{
};

class Child : public Parent 
{
};

void RemoveObj(Parent*& pObj)
{
    delete pObj;
    pObj = NULL;
}

int main()
{
    Parent* pPObj = new Parent;
    Child*  pCObj = new Child;
    pPObj = new Parent();
    pCObj = new Child();



    RemoveObj(pPObj);
    RemoveObj(pCObj); // This is line 32
    return 1;
}

但这会在第 32 行产生以下编译器错误:

错误 C2664:“RemoveObj”:无法将参数 1 从“子 *”转换为“父 *&”

我同意不允许从 Child** 转换为 Parent**。但是为什么也不允许这种转换呢?

4

5 回答 5

7

类型的对象Child*不能绑定到 a的原因与 a不能转换为 aParent*&的原因完全相同。允许它允许程序员(有意或无意地)在没有强制转换的情况下破坏类型安全。Child**Parent**

class Animal {};

class DangerousShark : public Animal {};

class CuteKitten : public Animal {};

void f(Animal*& animalPtrRef, Animal* anotherAnimalPtr)
{
    animalPtrRef = anotherAnimalPtr;
}

void g()
{
    DangerousShark myPet;
    CuteKitten* harmlessPetPtr;

    f(harmlessPetPtr, &myPet); // Fortunately, an illegal function call.
}

编辑

我认为一些混淆是由于“convert”和“conversion”这两个词的松散使用而产生的。

与可以重新分配的对象不同,引用不能被反弹,所以在引用的上下文中,当我们谈到转换时,我们只能关注初始化一个新的引用。

引用总是绑定到一个对象,从 OP 的问题来看,很明显他的目标是获得一个直接绑定到现有对象的引用。仅当用于初始化引用的对象与引用的类型兼容时,才允许这样做。本质上,只有当类型相同,或者对象的类型派生自引用的类型并且引用类型至少与初始化对象一样具有 cv 限定时。特别是,指向不同类型的指针不兼容引用,无论指向类型的关系如何。

在其他情况下,可以使用可以转换为引用类型的东西来初始化引用。但是,在这些情况下,引用必须是 const 而不是 volatile,并且转换将创建一个临时对象,并且引用将绑定到这个临时对象而不是原始对象。正如所指出的,这不适合 OP 激励示例的要求。

总之, aChild可以直接绑定到 aParent&但 aChild*不能直接绑定到 a Parent*&。AParent* const&可以用 a 初始化Child*,但引用实际上会绑定到Parent*从对象复制初始化的临时Child*对象。

于 2009-04-10T09:17:03.833 回答
4
  • 你的类没有虚函数。请参阅常见问题解答 20.7

  • 因为Parent *&是对指向Parent对象的指针的引用。您正在传递一个指向 a 的指针Child——这些是不兼容的类型。您可以将临时绑定到 const 引用,即,如果您将参数更改为:

    void RemoveObj(Parent* const& foo);

但是,您将无法对此做太多事情。

这只是一个测试代码,所以我没有制作任何虚拟析构函数。如果我在第二次调用 RemoveObj() 时理解正确,我会得到一个临时 Parent* 对象,它可以作为对函数的 const 引用传递。这个对吗?

我强烈建议您在标准 C++98 模式下运行以下程序,一次在中,然后在您注释掉foo(b)和取消注释后再次运行delete b。接下来,尝试放入一个virtualbefore ~s()。差异应该是不言自明的!

#include <iostream>
using namespace std;
struct s { 
  s() {cout << __func__ << endl; }
  ~s() {cout << __func__ << endl; } 
};

struct t : s { 
   t() {cout << __func__ << endl; }
   ~t() {cout << __func__ << endl; } 
};

void foo(s* const& x) { delete x; }

int main() {
 t* b = new t;
 foo(b);
 //delete b;
}
于 2009-04-10T09:09:11.497 回答
1

您可以转换 a Child* to a Parent*: 这会创建一个临时的。但是您不能将非常量引用绑定到该临时文件。

那不是问题**/*&/etc。您正在尝试做的事情是完全可以的并且是有道理的。鲨鱼大战小猫有同样的问题:不是混合小猫和鲨鱼的问题。您不能将非常量引用绑定到该未命名指针。

这不是Parent** vs. Child**问题:在那里,如果 aChild**是 a Parent**,那么可以分配p[0] = new NotAChild;。所有都是 A 子类型的对象的集合不是 A 的集合。

于 2009-04-10T09:43:20.667 回答
0

type*& 是 type** 的另一种语法形式, Parent*& 和 Child*& 以及 Parent** 和 Child** 彼此无关 - 这些是不在一个类层次结构中的不同类型。

于 2009-04-10T09:09:19.860 回答
0

由于 Dirk 提到的原因,这不起作用。如果您真的需要 RemoveObj 方法,那么我只需将您新分配的 Child 对象保存为 Parent*:

#include <iostream>

class Parent 
{
public:
    virtual ~Parent()
    {
        std::cout << "Parent destructor" << std::endl;
    }
};

class Child : public Parent 
{
public:
    virtual ~Child() 
    {
        std::cout << "Child destructor" << std::endl;
    }
};

void RemoveObj(Parent*& pObj)
{
    delete pObj;
    pObj = NULL;
}



int main (int argc, const char * argv[]) {

    Parent* pPObj = new Parent;
    Parent*  pCObj = new Child;

    RemoveObj(pPObj);    
    RemoveObj(pCObj); // This is line 32


    return 0;
}
于 2009-04-10T09:24:17.310 回答