免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: gp101224
打印 上一主题 下一主题

基类与继承类指针转换问题 [复制链接]

论坛徽章:
0
11 [报告]
发表于 2005-11-21 11:28 |只看该作者
原帖由 gvim 于 2005-11-21 11:25 发表


Car* c 和 Vehicle* v , 都是声明成指针.
如果是指针和对象,大概是这样:
type b;
type *a = &b;
这样才是候sir的说明a 是指针 b是对象.


这个没有本质区别啊, &b 是个指针的值啊。

是不是我学 c 的脑袋转不过来了,晕ing ....

论坛徽章:
0
12 [报告]
发表于 2005-11-21 11:38 |只看该作者
原帖由 win_hate 于 2005-11-21 11:20 发表
type A  指针指向一个 type B 对象本质上就是两种指针的转换,是吗?


在C++中,这个说法不成立,比如:


  1. #include <iostream>

  2. using namespace std;

  3. class a
  4. {
  5. public:
  6.         /*virtual*/ void f()
  7.         {
  8.                 cout << "in an";
  9.         }
  10. };

  11. class b : public a
  12. {
  13. public:
  14.         /*virtual*/ void f()
  15.         {
  16.                 cout << "in bn";
  17.         }
  18. };

  19. int main()
  20. {
  21.                 // 指针指向对象的情况
  22.         a *p2;
  23.         a a1;
  24.         b b1;
  25.         p2 = &a1;
  26.         p2->f();
  27.         p2 = &b1;
  28.         p2->f();

  29.         // 指针转换
  30.         b *p3;
  31.         p3 = &b1;
  32.         p2 = p3;
  33.         p2->f();

  34.         return 0;
  35. }

复制代码

没有把virtual关键字添加上的时候,输出的结果都是a,但是给f()添加了virtual关键字之后,输出变成了a,b,b因为指针和虚函数实现了动态的绑定,也就是所谓的多态....

论坛徽章:
2
亥猪
日期:2014-03-19 16:36:35午马
日期:2014-11-23 23:48:46
13 [报告]
发表于 2005-11-21 11:38 |只看该作者
我也好久没有用C__了,凭印象说的,可能不严谨,在这里道歉

论坛徽章:
0
14 [报告]
发表于 2005-11-21 11:45 |只看该作者
我再琢磨琢磨........

论坛徽章:
0
15 [报告]
发表于 2005-11-21 11:56 |只看该作者

  1. int main()
  2. {
  3.         // 指针指向对象的情况
  4.         p2 = &b1;
  5.         p2->f();

  6.         // 指针转换
  7.         b *p3;
  8.         p3 = &b1;
  9.         p2 = p3;
  10.         p2->f();
  11.   
  12. }
复制代码


还是没明白,只考虑后两个输出。


  1.   p2 = &b1;
  2.         p2->f();
复制代码


是直接把指针指向对象。


  1.         b *p3;
  2.         p3 = &b1;
  3.         p2 = p3;
  4.         p2->f();
复制代码


是通过指针进行转换。

但从输出的情况看,不加 virtual 是 a, a。 加了 virtual 是 b, b. 好象总是一样的?

论坛徽章:
0
16 [报告]
发表于 2005-11-21 12:09 |只看该作者
侯sir说得不太准确。

这取决于几个因素.

动态绑定---动态绑定是根据在执行时的实际对象调用相应的方法。

首先,类的方法只有是virtual的才有可能进行动态绑定;

其次,这还取决于编译器是否可以获得足够的类型信息,如果编译器获得的实际类型信息不
全面时,编译器只能对绑定进行延迟。否则,编译器尽可能早的决定调用的方法。

具体地说,
就是函数的参数以引用类型(引用或指针)的形式传递参数时,对于虚拟成元函数,编译器只能采用迟绑定的形式。
函数参数以值得形式传递时,无论是普通成元函数还是虚拟成元函数,编译器都采用早期绑定的形式。


  1. class Vehicle
  2. {
  3. public:
  4.     virual void Start();
  5.     void Stop();
  6. };

  7. class Car : public Vehicle
  8. {
  9. pubic:
  10.    virtual void Start();
  11.    void Stop();
  12. };

  13. void Move(Vehicle vehicle)
  14. {
  15.      vehicle.Start(); //这里,由于函数的参数是值类型,所以编译器可以获得足够的类型信息,采用早绑定
  16. }

  17. void Move(Vehicle& vehicle)
  18. {
  19.     vehicle.Start(); //这里,由于传递的是引用类型,同时Start方法是virtual德,所以采用动态绑定,实际调用的方法,取决于调用这个函数时,传入的实际对象的类型。
  20.     vehicle.Stop(); //这里,由于Stop方法是一个普通函数,无论如何也不会进行动态绑定。
  21. }
复制代码


至于采用这种策略,是因为性能要求,静态绑定的性能始终要比动态绑定的性能要高,编译器获得足够的类型信息时,尽可能采用静态绑定。

[ 本帖最后由 renstone921 于 2005-11-21 12:11 编辑 ]

论坛徽章:
0
17 [报告]
发表于 2005-11-21 12:17 |只看该作者
在Addison Wesley 出版的C++ FAQS, 2nd Edition中的FAQ 17.06中说:

Q:Can a derived class pointer be converted into a pointer to its public base class?
A:Such conversions are possible and don't even require a pointer cast.

class Vehicle { };
class Car : public Vehicle { };

void f(Vehicle* v) throw();

void g(Car* c) throw()
{
  f(c);  //Perfectly safe; no cast needed
}

私有继承表示的是"has a"的关系,亦即有一个的关系。

公有继承public derived-表示父类和子类是"is a"的关系,所以公有继承得出的子类的实例,必然是其公有基类的一个实例。所以,把子类实例的指针赋给基类的指针,是类型安全的,注意 is a关系。

论坛徽章:
0
18 [报告]
发表于 2005-11-21 12:24 |只看该作者
我吃饭的时候想了想,win_hate说的没错,两者是一样的情况,我扯到了别的东西上面了~~汗

论坛徽章:
0
19 [报告]
发表于 2005-11-21 12:25 |只看该作者
原帖由 win_hate 于 2005-11-21 11:56 发表
[code]
int main()
{
        // 指针指向对象的情况
        p2 = &b1;
        p2->f();

        // 指针转换
        b *p3;
        p3 = &b1;
        p2 = p3;
        p2->f(); ...


我吃饭的时候想了想,win_hate说的没错,两者是一样的情况,我扯到了别的东西上面了~~汗

论坛徽章:
0
20 [报告]
发表于 2005-11-21 12:33 |只看该作者
1、如果你以一个"基类之指针"指向一个"派生类之对象",那么经由该指针你只能调用该基类所定义的函数
2、如果你以一个“派生类之指针”指向一个“基类之对象”,你必须先做明显的转型操作(explicit cast),这种作法很危险,不符合真实生活经验,在设计上也许会带给程序员困惑;
3、如果积累和派生类都定义了“相同名称之函数”,那么通过对象指针调用成员函数时,到底调用了那个函数,必须视该指针的原始类型而定,而不是视指针实际所指的对象的类型而定,这与第1点其实意义相通。

侯sir在这样讲时,是基于mfc讲的,mfc作为一个在win3.1时代出现的c++框架,由于当时操作系统和硬件的限制,考虑到空间和时间效率,在mfc中尽量避免使用虚拟成元函数,第1个阐述的就是当你的成员函数根本就不是虚拟成元函数时,无论如何,编译器都采用静态绑定的策略,所以的除了上述结论--这个结论是基于完全放弃了使用虚拟成元函数动态绑定得出的。

2.用c语言描述一下。有下面两个结构

  1. struct Date
  2. {
  3.     int year;
  4.     int month;
  5.     int day;
  6. };

  7. struct DateTime
  8. {
  9.     int year;
  10.     int month;
  11.     int day;
  12.     int hour;
  13.     int minute;
  14.     int second;
  15. };

  16. void test()
  17. {
  18.     struct DateTime* dateTime = NULL;
  19.     struct Date* date = malloc(sizeof(struct Date));
  20.     date->year = 2005;
  21.     date->month = 21;
  22.     date->day = 1;
  23.    
  24.     dateTime = (struct DateTime *)date;
  25.     //现在dateTime获得了访问已&date的值开始,sizeof(struct DateTime)大小的存储空间并对齐进行
  26.    //解释的权利。他把它所指向的第一个4字节解释为year...诸如此类。

  27. }

复制代码


这也说明了这种向下转换不安全的原因。所以c++语言里面引入了dynamic_cast这个转换运算符,来保证进行安全的转换。

3.这还是基于不使用虚拟成元函数这一基本条件进行阐述的。这也是mfc为何如此丑陋的原因之一。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP