我试图通过将引用传递给指针而不是指向指针的指针来回答提到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 *'转换为 '父母*&'
我同意不允许从Child **转换为Parent **。但为什么不允许这种转换?
答案 0 :(得分: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而不是volatile,转换将创建一个临时引用,并且引用将绑定到此临时对象而不是原始对象。正如所指出的,这不适合OP的激励示例的要求。
总之,Child
可以直接绑定到Parent&
,但Child*
不能直接绑定到Parent*&
。可以使用Parent* const&
初始化Child*
,但引用实际上将绑定到从Parent*
对象初始化的临时Child*
对象。
答案 1 :(得分:4)
您的课程没有虚拟功能。请参阅FAQ 20.7
Beacuse Parent *&
是对指向Parent
对象的指针的引用。您正在传递指向Child
的指针 - 这些是不兼容的类型。您可以将临时绑定到const-reference,即如果将参数更改为:
void RemoveObj(Parent* const& foo);
但是你将无法做到这一点。
这只是一个测试代码,所以我没有制作任何虚拟析构函数。如果我在第二次调用RemoveObj()时理解正确,我会得到一个临时的Parent *对象,它可以作为const引用传递给函数。这是对的吗?
我强烈建议您在标准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;
}
答案 2 :(得分:1)
您可以转换Child* to a Parent*
:这会创建一个临时的。但是你不能将非const引用绑定到那个临时引用。
这不是**/*&/etc
的问题。你要做的是完全没问题,也是有道理的。 Shark vs. Kitten也有同样的问题:不是混合小猫和鲨鱼的问题。您不能将非const引用绑定到该未命名的指针。
这不是Parent** vs. Child**
问题:如果Child**
是Parent**
,则可以指定p[0] = new NotAChild;
。 A的所有子类型的对象集合不是A的集合。
答案 3 :(得分:0)
型*&安培;是**和Parent *&amp;的另一种句法形式。和孩子*&amp;与父**和子**无关 - 这些是不在一个类层次结构中的不同类型。
答案 4 :(得分: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;
}