代码之家  ›  专栏  ›  技术社区  ›  Naveen

从派生*转换为基*&

  •  6
  • Naveen  · 技术社区  · 16 年前

    我想回答上面提到的问题 here 通过将引用传递给指针,而不是像这样将指针传递给指针:

    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*”转换为 “父母们”

    我同意不允许从子**转换为父**。但为什么不允许这种转换呢?

    5 回复  |  直到 16 年前
        1
  •  7
  •   CB Bailey    16 年前

    类型的对象 Child* 无法绑定到 Parent*& 原因与 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.
    }
    

    编辑

    我认为,有些混淆是由于“转换”和“转换”这两个词的使用不当造成的。

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

    引用总是绑定到一个对象,从OP的问题可以很明显地看出,他希望得到一个直接绑定到现有对象的引用。仅当用于初始化引用的对象是 引用兼容 引用的类型。本质上,只有当类型相同,或者对象的类型是从引用的类型派生的,并且引用类型至少与初始化对象的cv相同时,才需要这样做。特别是,指向不同类型的指针与引用不兼容,不管指向类型的关系如何。

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

    综上所述,A Child 可以直接绑定到 Parent& 但一 儿童* 无法直接绑定到 母公司; . 一 Parent* const& 可以用初始化 儿童* ,但引用将实际绑定到 Parent* 从初始化的对象副本 儿童* 对象。

        2
  •  4
  •   dirkgently    16 年前
    • 您的类没有虚拟函数。见 FAQ 20.7

    • 灯塔 Parent *& 是指向指向 Parent 对象。您正在将指针传递给 Child --这些类型不兼容。可以将临时引用绑定到常量引用,即,如果将参数更改为:

      void RemoveObj(Parent* const& foo);

    但是这样你就不能做太多了。

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

    我强烈建议你在标准C++ 98模式下运行下面的程序,一次一次,一次又一次。 foo(b) 未注释 delete b . 接下来,尝试加入 virtual 之前 ~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;
    }
    
        3
  •  1
  •   Benoît    16 年前

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

    这不是问题 **/*&/etc . 你想做的事情完全没问题,而且很有意义。那只鲨鱼和小猫有着同样的问题:不是猫和鲨鱼混合的问题。不能将非常量引用绑定到该未命名指针。

    这不是 Parent** vs. Child** 问题:如果有 Child** 是一个 Parent** ,然后可以分配 p[0] = new NotAChild; . 所有子类型的对象集合不是的集合。

        4
  •  0
  •   sharptooth    16 年前

    类型*&是类型**的另一种语法形式,父级*&和子级*&以及父级**和子级**彼此不相关-它们是不同的类型,不在一个类层次结构中。

        5
  •  0
  •   Nick Haddad    16 年前

    因为德克提到的原因,这行不通。如果您真的需要一个removeobj方法,那么我将把您新分配的子对象保存为父对象*:

    #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;
    }