我应该使用 dynamic_cast<T> 进行复制吗?
Posted
技术标签:
【中文标题】我应该使用 dynamic_cast<T> 进行复制吗?【英文标题】:Should I be using dynamic_cast<T> for copying? 【发布时间】:2009-04-09 09:43:42 【问题描述】:更新 1:
更正了废话代码!感谢cmets,我做了第一个sn-p的哈希,哎呀。
更新 2:
还更新了问题标题,因为已指出答案没有必要使用 dynamic_cast。
我在这里尝试实现的是使用强类型的深层复制;我希望能够将 Class2 复制到 Class2 的另一个实例;但是,我也想使用基础 Class1 中的 CopyTo 函数。这个想法来自我的 C# 经验,通常我只是将返回类型设为通用(参见 C# sn-p)。
void Class1::CopyTo(Class1 *c1)
// Write data in to c1 from this instance.
c1->exampleData = exampleData;
// Class2 inherits Class1
Class2 *Class2::Copy()
Class2 *c2a = new Class2();
CopyTo(c2a);
Class2 *c2b = dynamic_cast<Class2*>(c2a);
return c2a;
这就是我在 C# 中的方式:
public class Class1
T Copy<T>()
where T : Class1
/* Can't remember the best way to do this in C#;
* basically if T was Class2 this would need to create
* a new instance of that class, and the same goes for
* Class1. */
T copy = createNewInstance();
// Copy the data from this to 'copy'.
copy.exampleData = exampleData;
return copy;
现在,和 C# sn-p 相比,C++ sn-p 感觉很臭。是否可以在没有指针的情况下执行此操作,或者这种方式是最佳实践?
【问题讨论】:
编辑后的代码仍然有点难以理解。对 CopyTo 的调用将崩溃,它传入一个未初始化的指针并调用它的方法。我确定这不是您的实际代码的工作方式,但您在这里所做的工作非常不清楚。 @r3n:正如 Dan Olson 所指出的,您的代码中有未定义的行为。您是否为内存管理而烦恼?你觉得 auto_ptr/unique_ptr 能解决这个问题吗? 我也会赶上潮流——抱歉,您更新的代码没有多大意义。特别是,CopyTo() 可能应该称为“CopyFrom()”,因为它不会更改其参数。无论发生什么,一旦 void* 出现,所有 dynamic_cast 的赌注都会被取消。 它现在执行从 Class2* 到 Class2* 的 dynamic_cast 吗?那是无操作。建议关闭、重新考虑和重新询问。 尝试在没有代码示例的情况下表达问题,然后添加一个全新的代码示例来说明问题。 【参考方案1】:我不清楚你在问什么,但请注意,当你说:
Class2 *c2 = dynamic_cast<Class2*>(c1);
强制转换的结果可能为 NULL,您必须对此进行检查。
【讨论】:
【参考方案2】:您应该在代码 sn-p 上多做一些工作。
GetSomethingCopy 正在创建一个 Class2 类型的指针,该指针被传递给 CopyTo。 CopyTo 尝试调用接收到的从未初始化过的指针的成员函数:segmentation fault and the program dies.
即使这没有杀死应用程序,您也试图将 dynamic_cast 从 Class2* 转换为 Class2*,这几乎什么都不做。如果您打算从 CopyTo 转换返回值,您必须知道您不能在 void* 上使用 dynamic_cast。您必须更改 CopyTo 签名以返回 Class1(以便您以后可以转换它)或在 void* 上使用 static_cast。
请注意,要么 Copy 是 Class1 中的虚函数,实际上在 Class2 中执行并创建了 Class2 对象,否则返回的元素将不是 Class2,而是 Class1。
CopyTo 方法的名称令人困惑,因为它不是将复制到参数,而是从参数复制。
毕竟,我仍然不知道你在问什么。您想在哪里使用堆栈内存?您可以将堆栈分配的元素传递给函数,但是将指针/引用返回到堆栈分配的元素又是一个分段错误:当函数结束时对象将被销毁,并且接收器将留下一个悬空的指针/引用。
现在,如果您的问题是关于是否可以在堆栈分配的元素上使用 dynamic_cast 的问题更具理论性,您可以(假设 Class2 继承自 Class1):
void f()
Class2 c2;
Class1 &c1 = c2; // c1 is a Class1 reference to a Class2 object
dynamic_cast<Class2&>(c1).class2method();
// or:
dynamic_cast<Class2*>(&c1)->class2method();
如果您更新代码,请在此答案中发表评论,以便我注意到并在今晚更正它。
【讨论】:
是的,你完全正确,我的错。我真的应该通过编译器运行它。现在应该有意义了…… 您更正了 CopyTo 参数未初始化的问题,但其余问题仍然存在。【参考方案3】:我不确定您要实现什么,因为代码仍然没有多大意义。但是,我相信以下内容应该近似于您正在尝试做的事情。请注意,我不使用堆内存:这不是必需的,而且会泄漏内存。
template <typename T>
T Class1::Copy()
T instance;
CopyTo(&instance);
return instance;
之所以有效,是因为您将指向instance
的(多态)指针传递给Class1
的CopyTo
方法。
然后你可以这样调用代码:
Class2 x1;
// Fill x1
Class2 x2 = x1.Copy<Class2>();
但是,此代码仍然 有异味,因为它不是惯用的 C++:在 C++ 中,您通常会编写一个复制构造函数。后期绑定的Copy
方法确实存在,但它们很少需要,而且上面不是后期绑定的(但您的 C# 代码也不是)。
【讨论】:
啊,所以我最好从构造函数中复制?那会更有意义......对吗? 重点是,在 C++ 中,x = y
的简单操作会进行复制,这与 C# 不同,因为所有变量都有值语义。所以,实现拷贝构造函数和赋值运算符是更好的解决方案。
我不太确定这个解决方案。对我来说,这个问题暗示了这个解决方案中丢失的虚拟克隆方法。如果是这样,你就无法避免指针。
@dribeas:C# 代码也不是虚拟克隆方法,它不会那样工作。事实上,代码是严格静态的。它所做的只是通过继承避免一些代码重复(这可能很好)。【参考方案4】:
该代码没有任何意义...无论如何我“猜想”如果您不使用 void* 作为返回值,您可以使用静态转换?
好的,现在代码有意义了。
你不需要任何动态转换,它已经是 Class2 类型了。
【讨论】:
抱歉,已更新。这是我快速拼凑起来的伪代码。【参考方案5】:在您的 CopyTo 函数中,您将返回一个指向在堆栈上创建的对象的指针 - 这是不可能的,因为该指针指向的对象将在函数返回时被销毁。
在回答您的问题时,您可以在指针或引用上使用dynamic_cast
。在您的情况下,我可能会使用 new
而不是在堆栈上分配要动态返回的对象,然后您可以安全地返回一个指针。但是,我倾向于将使用 dynamic_cast
视为潜在的代码异味,以及应该使用虚函数的标志。
【讨论】:
是的,感觉特别臭。我习惯了 C# 中的泛型类型,我只是想找到 C++ 的等价物。 C++ 中等价的泛型类型是模板。它们甚至有类似的尖括号符号,并且没有安全栏(非常强大,有时非常复杂)。或者你所说的“通用类型”到底是什么意思? 是什么让你说模板没有安全栏?它们在编译时被检查。与依赖于许多其他东西的运行时工件(其中的dynamic_cast)不同,模板在编译时进行验证。只是很难理解编译器的错误,但如果它编译它是安全的。【参考方案6】:不,dynamic_cast 仅适用于指针和引用。无论如何,您无法安全地返回您在堆栈上分配的任何内容,因此我不确定在这种情况下您打算如何修改代码。
【讨论】:
【参考方案7】:啊,现在问题很清楚了。从技术上讲,答案是否定的,不是 dynamic_cast,但我真的不明白你为什么想要它。看来你只是想要
void Class1::CopyTo(Class1& c1)
// Write data in to c1 from this instance.
c1.SomeIntValue = SomeIntValue;
// Class2 inherits Class1
Class2* Class2::Copy()
Class2 *c2 = new Class2();
CopyTo(*c2);
return c2;
//or more idiomatic
Class2 Class2::Copy()
Class2 c2
CopyTo(c2);
return c2;
【讨论】:
以上是关于我应该使用 dynamic_cast<T> 进行复制吗?的主要内容,如果未能解决你的问题,请参考以下文章
什么时候应该使用 static_cast、dynamic_cast、const_cast 和 reinterpret_cast?
什么时候应该使用 static_cast、dynamic_cast、const_cast 和 reinterpret_cast?