在C ++ for Windows中,我有一些对象工厂应该通过将指向对象的指针传递给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 **
编辑:第一个答案提到界面很可怕,看不到谁在分配等等......我怎样才能改进?答案 0 :(得分:8)
让我们考虑CreateInfoObject
的可能实现:
void InfoFactory::CreateInfoObject(AbstractInfo** info)
{
*info = new SuperInfo;
}
现在,SuperInfo
和MyInfoObject
没有任何共同点吗?
这就是为什么一般来说,以下是禁止的:
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);
如果myInfo
没有指向nullptr
(或派生)的实例,会将info
设置为MyInfoObject
。
请记住,你的界面非常可怕。它非常C-ish并且不清楚是否实际分配了内存......如果是,则负责处理内存。
修改强>:
在良好的 C ++风格中,我们使用RAII来表示所有权并确保清理。 RAII虽然不是很有说服力,但我自己更喜欢新的SBRM(Scope Bound Resources Management)。
这个想法是,不是使用裸指针,而是没有表明所有权(即你必须在其上调用删除吗?),你应该使用智能指针,例如unique_ptr
。
您还可以使用方法的return参数,以避免进行两步初始化过程(首先创建指针,然后使其指向对象)。这是一个简洁的例子:
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_cast
转换为dynamic_cast
。
答案 1 :(得分:3)
因为 将CreateInfoObject()
采用指针指向AbstractInfo
的指针,所以函数可能会返回不是<{1}}的实例/ em> AbstractInfo
的实例。因此,您最终可能会指向MyInfoObject
实际指向MyInfoObject
。{/ p>
DifferentInfoObject
更改为MyInfoObject *InfoObj
,它应该有效。不要使用AbstractInfo *InfoObj
以外的任何内容丢弃转换,因为您不确定dynamic_cast<>
是否返回该子类的实例。
答案 2 :(得分:3)
编译器告诉你什么是错的。当T和U彼此无关时,您无法将类型为T *的指针转换为U *类型的指针。这里,T = MyInfoObject *,U = AbstractInfo *,这些是两个不共享任何继承关系的不同指针类型。
答案 3 :(得分:2)
指向指针的指针不如指向对象的指针灵活。编译器将严格执行该类型,而不考虑继承树。
修复此代码的最安全方法是使用双重赋值:
MyInfoObject* InfoObj = NULL; // derived from AbstractInfo object
AbstractInfo* temp = NULL;
InfoFactory fc;
fc.CreateInfoObject(&temp);
InfoObj = dynamic_cast<MyInfoObject*>(temp);
答案 4 :(得分:0)
考虑在CreateInfoObject
中发生。
假设还有AbstractInfo
的另一个子类,请致电Foo
。
在CreateInfoObject
内,我们创建了一个新的Foo
并将其分配给*info
。 (允许向上翻。)
但我们现在在Foo
内部MyInfoObject**
,这是错误的。