1.4派生类向基类的类型转换

来源:互联网 发布:体育套利软件 编辑:程序博客网 时间:2024/06/02 21:24

1.4派生类向基类的类型转换

要理解在具有继承关系的类之间发生的类型转换,有三点非常重要:

  • 从派生类向基类的类型转换只对指针或引用有效;
  • 基类向派生类不存在隐式类型转换;
  • 和任何其他成员一样,派生类向基类的类型转换也可能会由于访问受限而变得不可行。

从派生类向基类的类型转换只对指针或引用有效

C++标准并没有明确规定派生类的对象在内存中如何分布,但我们可以认为继承自Base的Derived对象包含如图所示的两部分:

这里写图片描述

因为在派生类对象中含有与其基类对应的组成部分,所以我们能把派生类的对象当成基类的对象来使用,而且我们也能将基类的指针或引用绑定到派生类对象中的基类部分上。

这种转换通常称为派生类到基类类型转换。

和其他类型转换一样,编译器会隐式地执行派生类到基类类型转换。

#include <iostream>using namespace std;class Base{public:    int b1;protected:    int b2;private:    int b3;};class Derived: public Base{public:    int d1;protected:    int d2;private:    int d3;};int main(){    Base base1;    Derived derived1;    Base *ptr = &derived1;    Base &ref = derived1;    return 0;}

派生类向基类的自动类型转换只对指针或引用类型有效,在派生类类型和基类类型之间不存在这样的转换。

很多时候,我们确实希望将派生类对象转换成它的基类类型,但是这种转换的实际发生过程往往与我们期望的有所差别。

请注意,当我们初始化或赋值一个类类型的对象时,实际上是在调用某个函数。当执行初始化时,我们调用构造函数;而当执行赋值操作时,我们调用赋值运算符。这些成员通常包含一个参数,该参数的类型是类类型的const版本的引用。

因为这些成员接受引用作为参数,所以派生类向基类的转换允许我们给基类的拷贝/移动操作传递一个派生类的对象。当我们给基类的构造函数传递一个派生类对象时,实际允许的构造函数是基类中定义的那个,显然该构造函数只能处理基类自己的成员。类似的,如果我们将一个派生类对象赋值给一个基类对象,则实际运行的赋值运算符也是基类中定义的那个,该运算符同样也只能处理基类自己的成员。

即:当我们用一个派生类对象为一个基类对象初始化或赋值时,只有该派生类对象中的基类部分会被拷贝、移动或赋值,它的派生类部分将被忽略掉。

Derived derived;Base base(derived); //调用Base::Base(const Base&)构造函数base = derived; //调用Base::operator=(const Base&)

注意,上述操作会切掉派生类对象中的派生类自定义成员:

  • 调用基类的拷贝构造函数时,该函数只拷贝b1,b2,b3(基类的)三个成员,忽略d1,d2,d3(派生类的)三个成员。
  • 调用基类的赋值运算符函数时,该函数只将b1,b2,b3(基类的)三个成员赋值给base,忽略d1,d2,d3(派生类的)三个成员。

基类向派生类不存在隐式类型转换

之所以存在派生类向基类的类型转换,是因为每个派生类对象都包含一个基类部分,而基类的引用或指针可以绑定到该基类部分上。一个基类的对象既可以以独立的形式存在,也可以作为派生类对象的一部分存在。如果基类对象不是派生类对象的一部分,则它只含有基类定义的成员,而不含有派生类定义的成员。

因为一个基类的对象可能是派生类对象的一部分,也可能不是,所以不存在从基类向派生类的自动类型转换。

Base base2;Derived derived2;//error: invalid conversion from ‘Base*’ to ‘Derived*’ [-fpermissive]Derived *ptr2 = &base2; //error: invalid initialization of reference of type ‘Derived&’ from expression of type ‘Base’Derived &ref2 = base2; 

试考虑以下代码,看是否与我们期望的不同。

#include <iostream>using namespace std;class Base1{public:    void display() const    {        cout << "Base1::display()" << endl;    }};class Base2: public Base1{public:    void display() const    {        cout << "Base2::display()" << endl;    }};class Derived: public Base2{    void display() const    {        cout << "Derived::display()" << endl;    }};void fun1(Base1 *ptr){    ptr->display();}void fun2(Base1 &ref){    ref.display();}int main(){    Base1 base1;    Base2 base2;    Derived derived;    fun1(&base1); //Base1::display()    fun1(&base2); //Base1::display()    fun1(&derived); //Base1::display()    fun2(base1); //Base1::display()    fun2(base2); //Base1::display()    fun2(derived); //Base1::display()    return 0;}

原创粉丝点击