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を'子*'から'親*&'に変換できません

子**から親**への変換は許可されないことに同意します。しかし、なぜこの変換も許可されていないのですか?

4

5 に答える 5

7

型のオブジェクトは、をに変換できないのとまったく同じ理由で、に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であり、揮発性ではない必要があり、変換によって一時オブジェクトが作成され、参照は元のオブジェクトではなく、この一時オブジェクトにバインドされます。指摘したように、これはOPのやる気を起こさせる例の要件には適していません。

要約すると、aをに直接バインドすることはChildできますParent&が、aChild*をに直接バインドすることはできませんParent*&。AParent* const&はで初期化できますChild*が、参照は実際にはオブジェクトParent*からコピー初期化された一時オブジェクトにバインドされChild*ます。

于 2009-04-10T09:17:03.833 に答える
4
  • クラスには仮想関数がありません。FAQ20.7を参照してください

  • Beacuseは、オブジェクトParent *&へのポインタへの参照です。Parent-へのポインタを渡していますChild-これらは互換性のないタイプです。一時的なものをconst-referenceにバインドできます。つまり、パラメータを次のように変更した場合です。

    void RemoveObj(Parent* const& foo);

しかし、そうすると、これでは多くのことができなくなります。

これは単なるテストコードだったので、仮想デストラクタは作成しませんでした。RemoveObj()の2回目の呼び出しで正しく理解できれば、関数へのconst参照として渡すことができる一時的なParent*オブジェクトを取得します。これは正しいです?

foo(b)次のプログラムを標準のC++98モードで実行することを強くお勧めします。一度はコメントアウトしてコメントを外した後、もう一度実行してください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;
}
于 2009-04-10T09:09:11.497 に答える
1

変換できますChild* to a Parent*:これは一時的なものを作成します。ただし、非定数参照をその一時参照にバインドすることはできません。

それはの問題ではありません**/*&/etc。あなたがやろうとしていることは完全にOKであり、理にかなっています。そのサメ対子猫には同じ問題があります:子猫とサメを混ぜる問題ではありません。非定数参照をその名前のないポインターにバインドすることはできません。

これはParent** vs. Child**問題ではありません。aがである場合Child**は、Parent**を割り当てることができp[0] = new NotAChild;ます。すべてAのサブタイプであるオブジェクトのコレクションは、Aのコレクションではありません。

于 2009-04-10T09:43:20.667 に答える
0

type *&はtype **の別の構文形式であり、Parent *&とChild *&は、Parent**とChild**と同様に相互に関連していません。これらは、1つのクラス階層にない異なるタイプです。

于 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 に答える