返回指向对象的参数指针

Posted

技术标签:

【中文标题】返回指向对象的参数指针【英文标题】:Returning an argument pointer to an object 【发布时间】:2010-09-27 16:02:05 【问题描述】:

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

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

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

我想我现在可以如下创建一个 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**

编辑:第一个答案提到界面很糟糕,看不到谁在分配等等......我该如何改进?

【问题讨论】:

【参考方案1】:

让我们考虑一下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);

如果info 没有指向MyInfoObject 的实例(或派生的),则将myInfo 设置为nullptr

但请记住,您的界面真的很糟糕。它非常 C-ish,不清楚是否实际分配了内存......如果是,谁负责处理它。

编辑

good C++ 风格中,我们使用 RAII 来表示所有权并确保清理。 RAII 是众所周知的,虽然不是很有指示性,但我自己更喜欢新的 SBRM(范围绑定资源管理)。

这个想法是,而不是使用一个裸指针,它不表明任何关于所有权的事情(即你必须在它上面调用 delete 吗?)你应该使用一个智能指针,例如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();

无法编译,因为不使用 static_castdynamic_cast 就无法将 AbstractInfo* 转换为 MyInfoObject*

【讨论】:

@Tony:我已经编辑了我的答案并提出了替代界面的建议。 非常感谢您的解释。现在我仍然可以从检索到的 AbstractInfoPtr 向下转换为派生对象吗?这是个好主意吗? @Tony:是和不是。如果您的设计不需要向下转换,那就更好了,但是如果您被AbstractInfoPtr 卡住并且需要更多派生的对象,那么继续向下转换。没有设施(你不能直接使用static_cast)所以你必须手动完成:1.获取指针并将其转换,2.如果成功将其分配给另一个unique_ptr,3.从第一个释放它.如果是static_cast,您可以使用快捷方式 2。它给出:unique_ptr&lt;MyInfoObject&gt; myinfo ( static_cast&lt;MyInfoObject*&gt;(info.release()) );。对于dynamic_cast,这可能会失败,因此之前的测试。【参考方案2】:

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

MyInfoObject *InfoObj 更改为AbstractInfo *InfoObj,它应该可以工作。除了dynamic_cast&lt;&gt; 之外,不要放弃转换,因为您不确定CreateInfoObject() 返回该子类的实例。

【讨论】:

【参考方案3】:

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

【讨论】:

问题不在于指针。问题是CreateInfoObject() 可以返回一个AbstractInfo 的实例,它不是也是MyInfoObject 的一个实例,而调用代码无法确保不会发生这种情况。 为了强化这个答案:即使类 MyInfoObject 和 AbstractInfo 具有继承关系,指针类型 MyInfoObject* 和 AbstractInfo* 却没有。 @Jonathan:调用代码无论如何都是无效的。它是无效的,因为 T 和 U 与引用无关。编译器不允许你做这样的事情,除非你明确地转换指针,在这种情况下你可能会遇到你所指的问题。 @Jonathan:sellibitze 是正确的;即使不涉及继承,也会发生此错误。例如,void f(void **vp); int *i; f(&amp;i); 也是非法的,尽管int*void* 之间存在转换。 我只想再提一件事。将 Derived* 类型的指针转​​换为 Base* 可能涉及指针调整。这就是为什么 Derived* 和 Base* 不与引用相关的另一个原因,即使 Derived 和 Base 与引用相关。【参考方案4】:

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

修复此代码最安全的方法是使用双重赋值:

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

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

【讨论】:

即使他在不使用任何指针的情况下返回一个对象(例如,使用方法AbstractInfo InfoFactory::CreateInfoObject();)也会存在问题。他试图将一个抽象类转换为一个具体的子类,这会产生一个坏-形成的程序,不会编译。 @Jonathan,这个问题正是我在答案中使用dynamic_cast 的原因。如果返回的对象实际上不是 MyInfoObject,则转换将失败。【参考方案5】:

考虑一下CreateInfoObject 中发生的情况。

假设AbstractInfo 的另一个子类,调用Foo

CreateInfoObject 中,我们创建一个新的Foo 并将其分配给*info。 (允许向上转换)。

但是在外部,我们现在在 MyInfoObject** 内部有 Foo,这是错误的。

【讨论】:

以上是关于返回指向对象的参数指针的主要内容,如果未能解决你的问题,请参考以下文章

函数返回一个指向对象的指针,在这种情况下我应该返回啥?

为啥 const QString& 参数返回错误的 const char* 指向数据的指针

具有在参数中返回指针的函数的智能指针

类函数指针(不涵盖:返回类函数指针)

当函数返回时,指向超出范围的对象的 C++ 指针 - 为啥会这样?

从列表返回指向对象的指针