请帮助我理解以下问题。
请看下面的代码示例:
#include <iostream>
class Shape {
public:
virtual wchar_t *GetName() { return L"Shape"; }
};
class Circle: public Shape {
public:
wchar_t *GetName() { return L"Circle"; }
double GetRadius() { return 100.; }
};
int wmain() {
using namespace std;
auto_ptr<Shape> aS;
auto_ptr<Circle> aC(new Circle);
aS = aC;
wcout << aS->GetName() << L'\t' << static_cast<auto_ptr<Circle>>(aS)->GetRadius() << endl;
return 0;
}
为什么我不允许这样做:
static_cast<auto_ptr<Circle>>(aS)->GetRadius()
编译器(MSVCPP 11):
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory(911): error C2440: 'initializing' : cannot convert from 'Shape *' to 'Circle *'
1> Cast from base to derived requires dynamic_cast or static_cast
答案 0 :(得分:5)
auto_ptr
的行为与指针的行为不同。当Shape*
派生自Circle*
时,语言中有一些特殊规则允许Circle
为Shape
。向下转换并不完全是类型安全的,因为它依赖于用户提供的指针值实际上指向Shape
的{{1}}基类子对象,但标准允许它方便。 Circle
“只是”一个库类,没有等效的转换。
即使你能做到,也常常会出错。复制auto_ptr
时,原始文件将失去对资源的所有权。您的auto_ptr
会将static_cast
复制到临时值,因此auto_ptr
将被重置,并且当临时值(在表达式结尾处)时资源将被销毁。在你的例子中没问题,因为它会在aS
处被销毁,但一般来说你不想复制return
除了函数调用参数或返回值,以表示所有权的转移从呼叫者到被呼叫者,反之亦然。
您可以做的是auto_ptr
,或者更好的是重新构建代码以避免需要向下转发。如果您知道自己的对象是static_cast<Circle*>(aS.get())->GetRadius()
,请将其保存在Circle
[*]中。如果您将其保留在auto_ptr<Circle>
中,则不要依赖auto_ptr<Shape>
。
[*]或者,如果您的实现提供了它们,则可以使用更好的智能指针,例如Circle
,unique_ptr
或scoped_ptr
。即使你的实现没有提供它们,也有Boost。
答案 1 :(得分:3)
你肯定不想做那个演员,因为std::auto_ptr<T>
在用另一个类的实例初始化时取得内部指针的所有权。
aS
将松开指针,并且new Circle
对象将在std::cout
语句的末尾被销毁,因为对象指针现在归临时所有。
相反,你可能正在寻找类似下面的内容:
cout << ... << static_cast<Circle*>(aS.get ())->GetRadius() << endl;
您也可以将其转换为引用,如下所示:
cout << ... << static_cast<Circle&> (*aS).GetRadius () << endl;