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不存在。会出现严重后果。

posted @ 2014-08-05 11:36  树上的猪  阅读(10641)  评论(0编辑  收藏  举报