注:以下转换成功(包括隐式转换和强制转换)的前提均是——派生类向基类的转换是可访问的
隐式类型转换
基类的指针(包括智能指针)或引用可以绑定到派生类对象。即存在派生类向基类的隐式转换。反之,不存在。
Bulk_quote bulk; // 派生类对象
Quote item; // 基类对象
Quote *p = &bulk; // 正确
Bulk_quote *p1 = item; // 无法通过编译
基类和派生类的对象间不存在隐式类型转换。当我们初始化或者赋值一个类类型对象时,实际上是在调用某个函数。当执行初始化时,调用构造函数;当执行赋值操作时,调用赋值运算符。
故:使用派生类对基类初始化或赋值,能正常编译运行。反之无法通过编译。
Bulk_quote bulk; // 派生类对象
Quote item(bulk); // 调用Quote::Quote(const Quote&) 拷贝构造函数
item = bulk; // 调用Quote::operator=(const Quote&)
Bulk_quote bulk1(item); // 无法通过编译
Bulk_quote bulk2 = item; // 无法通过编译
继承关系的类之间发生类型转换,有三点非常重要:
- 从派生类向基类的类型转换只对指针或引用类型有效。
- 基类向派生类不存在隐式类型转换
- 和任何其他成员一样,派生类向基类的类型转换也可能会由于访问受限而变得不可行。
派生类向基类的转换是否可访问(或者说可行)由使用该转换的代码决定,同时派生类的派生访问说明符也会有影响。假定D继承自B:
- 只有当D公有地继承B时,用户代码才能使用派生类向基类转换;如果D继承B的方式是受保护的或者私有的,则用户代码不能使用该转换。
- 无论D以什么方式继承B,D的成员函数和友元都能使用派生类向基类的转换;派生类向其直接基类的类型转换对于派生类的成员和友元来说永远是可访问的。
- 如果D继承B的方式是公有的或者受保护的,则D的派生类的成员和友元可以使用D向B的类型转换;反之,如果D继承B的方式是私有的,则不能使用。
// 基类Quote
class Quote
{
public:
Quote();
virtual ~Quote();
}
// 公有继承的派生类Bulk_quote_pub
class Bulk_quote_pub : public Quote
{
public:
Bulk_quote_pub();
virtual ~Bulk_quote_pub();
}
// 私有继承的派生类Bulk_quote_pri
class Bulk_quote_pri : private Quote
{
public:
Bulk_quote_pri();
virtual ~Bulk_quote_pri();
}
int main()
{
Bulk_quote_pub bulk_pub; // 派生类对象
Bulk_quote_pri bulk_pri; // 派生类对象
Quote item; // 基类对象
Quote *p = &bulk_pub; // 正确,bulk_pub公有的继承了Quote,可以访问到基类
Quote *p1 = &bulk_pri; // 无法通过编译,bulk_pri私有的继承了Quote,无法访问到基类
return 0;
}
强制类型转换
该部分详见: static_cast和dynamic_cast详解。本文主要讨论的是在继承关系的类中的类型转换。
static_cast:
1、 基类(父类)和派生类(子类)之间指针或引用的转换。
- 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
- 进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
Bulk_quote *bulk_p = new Bulk_quote(); // 派生类指针
Quote *item_p = new Quote(); // 基类指针
Quote *item_p1 = static_cast<Quote *>(bulk_p); // 上行转换,安全
Bulk_quote *bulk_p1 = static_cast<Bulk_quote *>(item_p); // 下行转换,不安全,不要使用
2、基类和派生类对象间的转换。
- 派生类对象可以向基类对象转换
- 基类对象不可以向派生类转换
Bulk_quote bulk; // 派生类对象
Quote item; // 基类对象
Quote item1 = static_cast<Quote>(bulk); // 可以正常编译运行
Bulk_quote bulk1 = static_cast<Bulk_quote>(item); // 无法通过编译
dynamic_cast:
dynamic_cast的转换只有下面3种:
- dynamic_cast< type* >(e)
type必须是一个类类型且必须是一个有效的指针 - dynamic_cast< type& >(e)
type必须是一个类类型且必须是一个左值 - dynamic_cast< type&& >(e)
type必须是一个类类型且必须是一个右值
dynamic_cast主要用于类层次间的上行转换和下行转换。
在进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。使用dynamic_cast要耗费重大的运行成本。
Bulk_quote *bulk_p = new Bulk_quote(); // 派生类指针
Quote *item_p = new Quote(); // 基类指针
Quote *item_p1 = dynamic_cast<Quote *>(bulk_p); // 上行转换,同static_cast效果一样,但是运行代价更大
Bulk_quote *bulk_p1 = dynamic_cast<Bulk_quote *>(item_p); // 下行转换,失败,bulk_p1为空
Bulk_quote *bulk_p2 = dynamic_cast<Bulk_quote *>(item_p1); // 下行转换,成功
智能指针share_ptr的类型转换
static_cast和dynamic_cast用于非智能指针的类型转换。针对智能指针进行的类型转换需要使用下面4种转换:static_pointer_cast、dynamic_pointer_cast、const_pointer_cast、reinterpret_pointer_cast。它们的功能和static_cast、dynamic_cast、const_cast、reinterpret_cast类似,只不过转换的是智能指针std::shared_ptr,返回的也是std::shared_ptr类型。只有share_ptr存在类型转换。