2010-09-27 105 views
0

在C++ for Windows中,我有一些对象工厂应该通过将指向对象的指针传递给Create函数并返回创建的对象来创建一系列Info对象。将参数指针返回给对象

void CreateInfoObject(AbstractInfo** info); // The creation function 

AbstractInfo是一个基类,我们有许多类型的Info对象派生。

我认为如下我现在可以创建一个信息对象:

MyInfoObject* InfoObj = NULL; // derived from AbstractInfo object 
InfoFactory fc; 

fc.CreateInfoObject(&InfoObj); // Now I want to get my initialized pointer back 

但它说,它不能做投...什么是错的?

错误: 无法从MyInfoObject ** _ W64转换为AbstractInfo **

编辑:第一个答案中提到,该接口是可怕的,看不出谁的分配等等...我怎么能提高?

回答

8

让我们想想一个可能实现的CreateInfoObject

void InfoFactory::CreateInfoObject(AbstractInfo** info) 
{ 
    *info = new SuperInfo; 
} 

现在,SuperInfoMyInfoObject不具备共同的权利什么?

这就是为什么,在一般情况下,以下是被禁止的:

struct Base {}; 
struct D1: Base {}; 
struct D2: Base {}; 

int main(int argc, char* argv[]) 
{ 
    Base** base = nullptr; 
    D1* d = nullptr; 
    base = d; 
} 

因为这将使D1指向的东西无关。

有几种解决方案:

// 1. Simple 
AbstractInfo* info = nullptr; 
fc.CreateInfoObject(info); 

// 2. Better interface 
std::unique_ptr<AbstractInfo> info = fc.CreateInfoObject(); 

然后,如果你知道肯定地说,你,其实,一个MyInfoObject你可以使用:

MyInfoObject* myInfo = static_cast<MyInfoObject*>(info); 

,或者如果您不确定:

MyInfoObject* myInfo = dynamic_cast<MyInfoObject*>(info); 

如果有的话,它将设置myInfonullptrinfo未指向MyInfoObject(或派生)的实例。

但请记住,您的界面真的很可怕。它非常C-ISH,不清楚记忆是否真的被分配......以及谁负责处理它,如果是的话。

编辑

良好 C++风格,我们使用RAII双方表示所有权和确保清理。 RAII是众所周知的,虽然不是很具有说服力,但我自己更喜欢新的SBRM(Scope Bound Resources Management)。

的想法是,而不是使用裸指针,这并不表明任何所有权(即你必须调用删除就可以了?),你应该使用智能指针,例如像unique_ptr

您也可以使用方法的返回参数,以避免有两个步骤的初始化过程(首先创建指针,然后使其指向一个对象)。这里有一个简明的例子:

typedef std::unique_ptr<AbstractInfo> AbstractInfoPtr; 

// Note: if you know it returns a MyInfoObject 
// you might as well return std::unique_ptr<MyInfoObject> 
AbstractInfoPtr InfoFactory::CreateInfoObject() 
{ 
    return AbstractInfoPtr(new MyInfoObject()); 
} 

// Usage: 
int main(int argc, char* argv[]) 
{ 
    InfoFactory factory; 
    AbstractInfoPtr info = factory.CreateInfoObject(); 

    // do something 

} // info goes out of scope, calling `delete` on its pointee 

在这里,关于所有权没有歧义。

此外,请注意你如何更好地理解你的问题在这里:

std::unique_ptr<MyInfoObject> info = factory.CreateInfoObject(); 

不会编译,因为你不能转换AbstractInfo*MyInfoObject*,而无需使用static_castdynamic_cast

+0

编辑讨论您的评论! – 2010-09-27 21:10:18

+1

@Tony:我编辑了我的答案,提供了一个替代接口的提案。 – 2010-09-28 06:28:24

+0

非常感谢您的解释。现在我还可以从检索到的AbstractInfoPtr中进行下拉式转换为派生的对象吗?这是一个好主意吗? – 2010-09-28 07:25:38

2

指向指针的指针并不像指向对象的指针那么灵活。编译器将严格执行该类型而不考虑继承树。

为了解决这个问题最安全的方法是使用双重任务:

MyInfoObject* InfoObj = NULL; // derived from AbstractInfo object 
AbstractInfo* temp = NULL; 
InfoFactory fc; 

fc.CreateInfoObject(&temp); 
InfoObj = dynamic_cast<MyInfoObject*>(temp); 
+0

即使他没有使用任何指针返回一个对象(例如使用'AbstractInfo InfoFactory :: CreateInfoObject();'。)方法,问题就会存在。)他正试图将一个抽象类转换为其具体子类,从而产生病态形成的程序,不会编译。 – 2010-09-27 16:12:39

+0

@Jonathan,这个问题正是我在答案中使用'dynamic_cast'的原因。如果返回的对象实际上不是'MyInfoObject',则投射将失败。 – 2010-09-27 17:23:29

3

因为CreateInfoObject()需要一个指针到一个指针到一个AbstractInfo,这是可能的函数返回一个实例AbstractInfo不是MyInfoObject的一个实例。所以你最终可能会得到一个指向MyInfoObject的指针,即实际上是指向一个DifferentInfoObject

更改MyInfoObject *InfoObjAbstractInfo *InfoObj它应该工作。除了dynamic_cast<>之外,不要抛弃转换,因为您不确定CreateInfoObject()是否会返回该子类的实例。

3

编译器告诉你什么是错的。当T和U彼此无关时,不能将T *类型的指针转​​换为U *类型的指针。在这里,T = MyInfoObject *,U = AbstractInfo *,这两个不同的指针类型不共享任何继承关系。

+1

问题不在于指针。问题是'CreateInfoObject()'可能返回'AbstractInfo'的实例,它不是* MyInfoObject的一个实例,调用代码无法确保不会发生。 – 2010-09-27 16:14:02

+0

为了强化这个答案:即使类MyInfoObject和AbstractInfo具有继承关系,指针类型MyInfoObject *和AbstractInfo也不会。 – 2010-09-27 17:39:21

+1

@Jonathan:调用代码无论如何是无效的。它是无效的,因为T和U不是参考相关的。编译器不会让你做这样的事情,除非你明确地转换了指针,在这种情况下你可能遇到你指的问题。 – sellibitze 2010-09-27 17:40:08

0

考虑到发生在CreateInfoObject

假设有另一个AbstractInfo的子类别,请致电Foo

里面CreateInfoObject我们创建一个新的Foo并将其分配给*info。 (允许upcast)。

但我们现在有FooMyInfoObject**里面,这是错误的。