基类和派生类互转
一.基类和派生类的裸指针转换
- 派生类的地址或者指针赋值给基类指针时,一般都用隐式转换。(也可以使用static_cast、dynamic_cast显示转换)
反过来,
-
基类指针赋值给派生类指针的时候,必须要显示转换:使用dynamic_cast或者static_cast。
-
例子如下
#include <iostream>
class base
{
public:
virtual ~base(void) = default;
};
class derived :public base
{
};
int main()
{
// 派生类对象
derived derivedobj;
std::cout << "派生类对象的地址是:" << &derivedobj << std::endl;
//===========================派生类的地址或者指针赋值给基类指针===========================//
// 基类裸指针保存派生类对象的地址(隐式转换)
base *pointer1 = &derivedobj;
std::cout << "隐式转换后,基类裸指针保存的地址是:" << pointer1 << std::endl;
// 基类裸指针保存派生类对象的地址(static_cast转换)
base *pointer2 = static_cast<base *>(&derivedobj);
std::cout << "static_cast转换后,基类裸指针保存的地址是:" << pointer2 << std::endl;
//============================基类指针赋值给派生类指======================================//
// 用派生类裸指针保存基类裸指针的值(不能隐式转换)
//derived *pointer3 = pointer1; // 去掉注释将会编译报错
// 用派生类裸指针保存基类裸指针的值(dynamic_cast转换)
derived *pointer4 = dynamic_cast<derived *>(pointer1);
std::cout << "dynamic_cast转换后,派生类裸指针保存的地址是:" << pointer4 << std::endl;
// 用派生类裸指针保存基类裸指针的值(static_cast转换)
derived *pointer5 = static_cast<derived *>(pointer1);
std::cout << "static_cast转换后,派生类裸指针保存的地址是:" << pointer5 << std::endl;
return 0;
}
- 从输出结果可以看出,都是同一个内存地址
派生类对象的地址是:003AFA0C
隐式转换后,基类裸指针保存的地址是:003AFA0C
static_cast转换后,基类裸指针保存的地址是:003AFA0C
dynamic_cast转换后,派生类裸指针保存的地址是:003AFA0C
static_cast转换后,派生类裸指针保存的地址是:003AFA0C
二.dynamic_cast和static_cast的区别
1、对于有继承关系的两个类:
- (1)子类转成父类dynamic_cast和static_cast都没有问题.
- (2)父类转成子类,dynamic_cast要求父类中有虚函数,否则编译不通过。static_cast不作此要求,编译通过。
在有虚函数的前提下,如果父类指针的确实指向的是子类实例,dynamic_cast转换成功,否则返回NULL;
static_cast对于转换前的指针是否指向实际子类实例,不作要求,都能转换成功。 - (3)在编译通过成功,且返回指针不为空的前提下,两者转换结果指针,都可以用来读写父类和子类成员函数和变量。
- (4)如果父类指针不是指向子类实例,编译都能通过,但是dynamic_cast返回为NULL,不能操作对象;
static_cast返回非空指针,可以读写父类成员变量,也能调用其成员函数。子类的成员函数可以调用,但是当操作涉及子类成员变量时候,不成功。
2、对于没有继承关系的两个类
static_cast可以编译通过,dynamic_cast不能编译通过。两者都不能读写成员变量和函数。
3、demo(即二.1.(4))
#include <iostream>
class base
{
public:
virtual ~base(void) = default;
};
class derived :public base
{
public:
int a=0;
};
class test : public base
{
};
int main()
{
std::cout << std::boolalpha;
// 两个不同的派生类对象
derived derivedobj;
test testobj;
// 基类裸指针保存派生类对象的地址
base *pointer = &derivedobj;
test *pointer2 = dynamic_cast<test *>(pointer);
std::cout << "test指针保存derivedobj的地址(dynamic_cast):" << std::endl;
std::cout << "是否成功:" << (pointer2 != nullptr) << std::endl << std::endl;
test *pointer4 = static_cast<test *>(pointer);
std::cout << "test指针保存derivedobj的地址(static_cast):" << std::endl;
std::cout << "是否成功:" << (pointer4 != nullptr) << std::endl << std::endl;
return 0;
}
输出结果
test指针保存derivedobj的地址(dynamic_cast):
是否成功:false
链接: dynamic_cast和static_cast的异同.
其他
-
由于dynamic_cast是在运行的时候进行检测和转换的,所以会影响到运行时的性能。
-
不过,在良好的程序设计中,不会也不应该频繁地将基类指针(或引用)转换成派生类指针(或引用),也就是说,不应该频繁地使用dynamic_cast。如果你的代码频繁地使用了dynamic_cast,应该要考虑,继承是否设计好。
-
因此,在良好的程序设计中,合理使用dynamic_cast是没有问题的。
三.dynamic_cast 转换引用
- dynamic_cast转换指针时,转换失败可以通过nullptr来表达。
- 而引用就不能。所以dynamic_cast转换引用使用的是抛出异常std::bad_cast。std::bad_cast在标准库typeinfo中。
#include <iostream> // std::cout std::endl
#include <typeinfo> // std::bad_cast
class base
{
public:
virtual ~base(void) = default;
};
class derived : public base
{
};
class test : public base
{
};
int main(void)
try
{
std::cout << std::boolalpha;
// 两个不同的派生类对象
derived derivedobj;
test testobj;
// 基类引用保存派生类对象
base &object = derivedobj;
derived &ref1 = dynamic_cast<derived &>(object);
std::cout << "成功转换成derived引用" << std::endl;
test &ref2 = dynamic_cast<test &>(object);
std::cout << "成功转换成test引用" << std::endl;
return 0;
}
catch (const std::bad_cast &)
{
std::cout << "转换失败" << std::endl;
}
四.基类和派生类的智能指针转换
- 基类和派生类的智能指针转换要使用std::dynamic_pointer_cast和std::static_pointer_cast。
- (std::dynamic_pointer_cast和dynamic_cast原理一样,std::static_pointer_cast和static_cast原理一样。)
参考
1.基类和派生类相互转换.