C++基类和派生类之间的转换
本文讲解内容的前提是派生类继承基类的方式是公有继承,关键字public
以下程序为讲解用例。
1 #include<iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 A(int m1, int n1):m(m1), n(n1){} 8 void display(); 9 private: 10 int m; 11 int n; 12 }; 13 14 void A::display() 15 { 16 cout << "m = " << m << endl; 17 cout << "n = " << n << endl; 18 } 19 20 class B :public A 21 { 22 public: 23 B(int m1, int n1, int p1) :A(m1, n1), p(p1){} 24 void display(); 25 private: 26 int p; 27 }; 28 29 void B::display() 30 { 31 A::display(); 32 cout << "p = " << p << endl; 33 } 34 35 void print1(A& a) 36 { 37 a.display(); 38 } 39 40 void print2(B& b) 41 { 42 b.display(); 43 } 44 45 void print3(A a) 46 { 47 a.display(); 48 } 49 50 void print4(B b) 51 { 52 b.display(); 53 } 54 55 int main() 56 { 57 A a(3, 4); 58 // a.display(); 59 B b(10, 20, 30); 60 // b.display(); 61 62 A * pa; 63 B * pb; 64 pa = &a; 65 // pa->display(); 66 pb = &b; 67 // pb->display(); 68 69 // pa = &b; 70 // pa->display(); 71 72 // pb = &a; //错误。派生类指针不能指向基类对象。 73 74 // print1(b); 75 // print2(a); //错误。不能用基类对象给派生类引用赋值。 76 // print3(b); 77 // print4(a); //错误。不能用基类对象给派生类对象赋值。 78 79 // pb = pa; //不能用基类指针给派生类指针赋值。 80 81 pb = (B*)pa; //可以强制转换,但是非常不安全。 82 pb->display(); //出现安全问题,p无法访问,因为a中没有p成员 83 system("pause"); 84 return 0; 85 }
切记:派生类对象是基类对象,派生类中包含有基类的成员。基类对象不是派生类对象,它不能包含派生类型的成员。
/**************派生类到基类的转化**************/
1。派生类对象地址赋值给基类指针
main函数中执行以下代码
1 A a(3, 4); 2 // a.display(); 3 B b(10, 20, 30); 4 // b.display(); 5 6 A * pa; 7 // B * pb; 8 // pa = &a; 9 // pa->display(); 10 // pb = &b; 11 // pb->display(); 12 13 pa = &b; 14 pa->display(); //会输出 10 20
pa为基类指针,指向派生类对象是合法的,因为派生类对象也是基类对象。语句会输出派生类对象中基类部分。
注意:这里并不会调用派生类的display函数,调用的是基类的display函数,因为指针pa是基类指针,编译器在编译阶段只知道pa的类型。如果要实现调用派生类的display函数,
需要用到虚函数实现多态性。之后的文章会讲到。
进一步解释一下编译时和运行时的区别。
编译时编译器能知道pa的类型为A *,但是不知道它指向了哪个对象,假如有以下语句
1 A a(3, 4); 2 B b(10, 20, 30); 3 A* pa; 4 int number; 5 cin >> number; 6 if (number >= 0) 7 pa = &a; 8 else 9 pa = &b;
pa指向的对象类型依赖于输入,运行时才输入,所以编译器是没有办法知道pa指向哪个类型的。
2.派生类对象赋值给基类引用
/**引用跟指针基本没有区别,引用本质上是指针,是个指针常量,具体可以参照我的另一篇C++中的引用和指针的联系和区别**/
main函数中执行以下代码
1 A a(3, 4); 2 B b(10, 20, 30); 3 print1(b); //会输出 10 20
形参为基类引用,实参为派生类对象,派生类对象也是基类对象,可以赋值给基类引用。输出派生类中基类部分。
注意:此时对象本身并未复制,b仍然是派生类对象,前面说过了引用就是一个指针。
3.派生类对象赋值给基类对象。
A a(3, 4); B b(10, 20, 30); print3(b);
派生类对象基类部分被复制给形参。
注意:实际上没有从派生类对象到基类对象的直接转换。对基类对象的赋值或初始化,实际上在调用函数,初始化时调用构造函数,赋值时调用赋值操作符。
/********************基类到派生类的转化******************/
切记:这种转换有可能引发严重的安全问题,编写代码时不要使用。没有基类到派生类的自动转换,原因在于基类对象只能是基类对象,不能包含派生类型的成员。
如果允许用基类对象给派生类对象赋值,那么就可以试图使用该派生类对象访问不存在的成员。
1 A a(3, 4); 2 B b(10, 20, 30); 3 A * pa; 4 B * pb; 5 // print2(a); //错误。不能用基类对象给派生类引用赋值。 6 // print4(a); //错误。不能用基类对象给派生类对象赋值。 7 // pb = &a; //错误。派生类指针不能指向基类对象。 8 9 pa = &a; 10 pb = &b; 11 12 //pb = pa; //错误。不能用基类指针给派生类指针赋值。 13 14 pb = (B*)pa; //可以强制转换,但是非常不安全。 15 pb->display(); //出现安全问题,p无法访问,因为a中没有p成员
注意到我们使用强制转换时,当派生类添加了基类中不存在的成员时,会出现安全问题。
pb->display();会调用派生类的display函数,但是它指向的内存是基类对象a的内存,p不存在。会出现严重后果。