是否可以通过向下转换从基础对象创建派生对象?
Is it possible to creat a derived object from a base object through downcast?
基地class
class Base
{
public:
Base()=default;
virtual void f(){cout << "base class\n";}
virtual ~Base(){}
};
派生class
class Derive : public Base
{
public:
Derive()=default;
void f() override {cout << "derived class\n";}
};
主要功能
int main()
{
Base* bsd = new Base;
Derive* dru = new Derive;
if(Derive* drd=dynamic_cast<Derive*>(bsd)){
drd->f();
cout << "downcast successful\n";
}else{
cout << "downcast failed\n";
}
if(Base* bsu=dynamic_cast<Base*>(dru)){
bsu->f();
cout << "upcast successful\n";
}else{
cout << "upcast failed\n";
}
delete bsd;
delete dru;
}
事实证明upcast 工作正常,而downcast 失败了。这么说好像也有道理。如果派生 class 包含未在基 class 中声明的成员对象并且没有默认构造函数,那么在向下转型期间会发生什么?
此外,通过dynamic_cast
创建的目标指针*drd(*bsd)
与要转换的指针*bsu(*dru)
指向同一个对象。所以删除一次就足够了。我们不会有悬空指针吧?
转换不会以任何方式创建任何新对象或更改对象。转换会更改现有对象的解释,因此如果对象不是 Derive
,则无法通过转换使其成为 Derive
。
请注意 Derive
也是 Base
,因为继承会创建 "is a" 关系。这就是向上转型起作用的原因:它所做的只是告诉编译器它应该将指向的对象视为 Base
,即使该对象实际上是 Derive
.
这对您的程序没有任何影响,因此这里是强制转换的说明:
class Derive : public Base
{
public:
Derive()=default;
void f() override {cout << "derived class\n";}
void added() {cout << "hello" << endl; }
};
dru->added(); // Works
Base* bsu=dynamic_cast<Base*>(dru);
bsu->added(); // Does not compile
本质上,强制转换 "hides" 从编译器添加了接口,命令它像 Base
一样对待 Derive
对象。当然,覆盖继续被正确调用,因为覆盖的成员函数是 Base
接口的一部分。
我不确定我是否答对了你的问题,但是是的,如果析构函数是 virtual
.
,你可以使用基指针安全地删除派生的 class
一个"dangling pointer"是不一样的:当你删除了bsd
和dru
之后,你就不能再使用这些指针了,它们变成了悬挂指针,因为它们指向的是被删除的指针你不再拥有的记忆。
dynamic_cast 检查 运行 时间类型检查并提供安全转换。如果有意义的转换是可能的,那么在沮丧之后你会得到一个有效的对象。正如您所指出的,在您的示例中,基础对象是 "base class"。 运行 时间类型检查失败。但是,如果基础对象是 "derieved class" dynamic_cast 就会成功。例如:
class CBase
{
public:
CBase(void);
virtual ~CBase(void);
virtual void identify()
{
std::cout << "base class" << std::endl;
}
};
class CDerieved : public CBase
{
public:
CDerieved(void);
virtual ~CDerieved(void);
virtual void identify()
{
std::cout << "Derieved class" << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CDerieved* pderieved = new CDerieved;
pderieved->identify();
CBase* pb = static_cast<CBase*>(pderieved);
pb->identify();
CDerieved* pd1 = dynamic_cast<CDerieved*>(pb);
pd1->identify();
return 0;
}
上面的代码会成功。
但请记住,如果您发现需要降压,则设计需要修改。
基地class
class Base
{
public:
Base()=default;
virtual void f(){cout << "base class\n";}
virtual ~Base(){}
};
派生class
class Derive : public Base
{
public:
Derive()=default;
void f() override {cout << "derived class\n";}
};
主要功能
int main()
{
Base* bsd = new Base;
Derive* dru = new Derive;
if(Derive* drd=dynamic_cast<Derive*>(bsd)){
drd->f();
cout << "downcast successful\n";
}else{
cout << "downcast failed\n";
}
if(Base* bsu=dynamic_cast<Base*>(dru)){
bsu->f();
cout << "upcast successful\n";
}else{
cout << "upcast failed\n";
}
delete bsd;
delete dru;
}
事实证明upcast 工作正常,而downcast 失败了。这么说好像也有道理。如果派生 class 包含未在基 class 中声明的成员对象并且没有默认构造函数,那么在向下转型期间会发生什么?
此外,通过dynamic_cast
创建的目标指针*drd(*bsd)
与要转换的指针*bsu(*dru)
指向同一个对象。所以删除一次就足够了。我们不会有悬空指针吧?
转换不会以任何方式创建任何新对象或更改对象。转换会更改现有对象的解释,因此如果对象不是 Derive
,则无法通过转换使其成为 Derive
。
请注意 Derive
也是 Base
,因为继承会创建 "is a" 关系。这就是向上转型起作用的原因:它所做的只是告诉编译器它应该将指向的对象视为 Base
,即使该对象实际上是 Derive
.
这对您的程序没有任何影响,因此这里是强制转换的说明:
class Derive : public Base
{
public:
Derive()=default;
void f() override {cout << "derived class\n";}
void added() {cout << "hello" << endl; }
};
dru->added(); // Works
Base* bsu=dynamic_cast<Base*>(dru);
bsu->added(); // Does not compile
本质上,强制转换 "hides" 从编译器添加了接口,命令它像 Base
一样对待 Derive
对象。当然,覆盖继续被正确调用,因为覆盖的成员函数是 Base
接口的一部分。
我不确定我是否答对了你的问题,但是是的,如果析构函数是 virtual
.
一个"dangling pointer"是不一样的:当你删除了bsd
和dru
之后,你就不能再使用这些指针了,它们变成了悬挂指针,因为它们指向的是被删除的指针你不再拥有的记忆。
dynamic_cast 检查 运行 时间类型检查并提供安全转换。如果有意义的转换是可能的,那么在沮丧之后你会得到一个有效的对象。正如您所指出的,在您的示例中,基础对象是 "base class"。 运行 时间类型检查失败。但是,如果基础对象是 "derieved class" dynamic_cast 就会成功。例如:
class CBase
{
public:
CBase(void);
virtual ~CBase(void);
virtual void identify()
{
std::cout << "base class" << std::endl;
}
};
class CDerieved : public CBase
{
public:
CDerieved(void);
virtual ~CDerieved(void);
virtual void identify()
{
std::cout << "Derieved class" << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CDerieved* pderieved = new CDerieved;
pderieved->identify();
CBase* pb = static_cast<CBase*>(pderieved);
pb->identify();
CDerieved* pd1 = dynamic_cast<CDerieved*>(pb);
pd1->identify();
return 0;
}
上面的代码会成功。
但请记住,如果您发现需要降压,则设计需要修改。