您的位置:首页 > 编程语言

面向对象编程系列三:基类和派生类之间的转换

2013-06-29 19:35 309 查看
在面向对象编程系列二中,我们看到派生类对象中包含了其基类的部分,可以说派生类对象也是基类对象,所以存在从派生类类型引用或指针到基类类型引用或指针的自动转换,即可以将派生类对象的引用转换为基类子对象的引用,指针也类似。

但是,由于基类类型对象可能是一个独立的对象也可能是一个派生类对象的子对象,所以不可以将基类类型的引用或指针转换为派生类类型的引用或指针。

相对于引用或指针的转换,对象的转换更为复杂。一般而言,可以使用派生类类型的对象对基类类型的对象进行初始化或赋值,但不可以将派生类类型对象直接转换为基类类型对象。下面分别简要讲述从派生类到基类的引用转换和对象转换:

(1)引用转换

如下面的代码:

double print(const Item_Base&);

Bulk_Item bulk;
print(bulk);//ok!将派生类对象传递给需要基类对象引用的函数


上面的代码中将派生类对象bulk传给了需要基类引用的函数,这是合法的。但是这种传递并没有导致派生类对象到基类对象的转换,它是将引用直接绑定到派生类对象,而该对象本身并没有被复制,也没有改变,它仍是派生类对象。

(2)对象转换

如果是将派生类对象而非引用或指针传递给需要基类对象的函数,情况则与上不同。此时,形参的类型是固定的——基类类型对象,传递进来的派生类对象的基类部分会被复制到形参,派生类部分则会被忽略。

用派生类对象的基类部分对基类对象进行初始化或赋值,实质上调用的是基类的构造函数或赋值操作符。 基类可以显式的定义将派生类对象复制或赋值给基类对象的构造函数和赋值函数:

class Base
{
  public:
    Base(const Derived&);
    Base &operator=(const Derived&);
};


但是上面的情况并不常见,通常这种转换是通过调用基类定义的本身之间的复制构造函数和赋值函数实现的:

class Base
{
  public:
    Base(const Base&);
    Base& operator=(const Base&);
};


将派生类对象转为基类对象一般需要以下几个步骤:

a. 将派生类对象对于转为基类类型引用,即将一个基类引用绑定到派生类对象上;

b. 将该引用作为实参传给基类的复制构造函数或赋值操作符,复制构造函数或赋值操作符使用派生类对象的基类部分对基类对象的成员进行初始化或赋值;

c. 复制构造函数或赋值操作符操作结束后,对象即为基类对象,它包含派生类对象的基类部分的副本,派生类对象自身的成分被忽略,产生的基类对象不包含派生类对象的成员,也没有派生类成员的存储空间。

(3)对象转换的可访问性

如果是public继承,用户代码和后代类都可以使用派生类到基类的转换;如果是protected继承或private继承,则用户代码不可以将派生类对象转为基类对象;如果是private继承,则不可以将派生类的后代类对象转为基类对象;如果是protected继承,则可以将派生类的后代类对象转为基类对象。

(4)基类到派生类的转换

不存在基类到派生类的自动转换,因为基类对象并不包含派生类的成员,甚至将绑定到派生类对象的基类引用或指针重新赋给派生类引用和指针时也是不合法的:

Derived dc;
Base* bp = &dc;//Ok,将基类指针指向派生类对象是合法的
Derived* cp = bp;//error,将基类指针赋给派生类指针是不合法的


这是因为编译器在编译时无法知道该转换在运行时是安全的,只能根据静态类型进行判断。如果用户可以确定这种转换在运行时是安全的,那么可以通过static_cast强制编译器进行转换,或者使用dynamic_cast申请在运行时检查,即使用下面的转换可以实现将基类指针到派生类指针的转换(须确保这种转换是运行时安全的):

Derived* cp = static_cast<Derived*>(bp);

Derived* cp = dynamic_cast<Derived*>(bp);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: