打印

翻了一下前几年的C++教案,觉得还有点价值,将摘选帖出

[复制链接]
楼主: HWM
手机看帖
扫描二维码
随时随地手机跟帖
41
一朝成名| | 2008-4-18 12:43 | 只看该作者 回帖奖励 |倒序浏览

LZ一页开一帖

版主无奈就全加裤子

那LZ可以批发裤子了呵呵

使用特权

评论回复
42
pigjiang| | 2008-4-18 20:14 | 只看该作者

做个记号,待闲时细读。

使用特权

评论回复
43
eleclike| | 2008-4-18 23:36 | 只看该作者

顶一下,慢慢看

留个位置,慢慢看

使用特权

评论回复
44
nwb1010| | 2008-4-19 02:02 | 只看该作者

hehe

使用特权

评论回复
45
wjun| | 2008-4-19 19:55 | 只看该作者

受教了

使用特权

评论回复
46
e的时代| | 2008-4-19 23:53 | 只看该作者

听课

下来听教授讲课

使用特权

评论回复
47
rainmans| | 2008-4-20 08:35 | 只看该作者

学习了,谢谢

使用特权

评论回复
48
ddc21ic| | 2008-4-20 21:46 | 只看该作者

好帖!

使用特权

评论回复
49
fiann| | 2008-4-20 22:44 | 只看该作者

take a look

````````````

使用特权

评论回复
50
sanwa_chen| | 2008-4-21 11:02 | 只看该作者

C++还是好啊

专心听课......

使用特权

评论回复
51
HWM|  楼主 | 2008-4-21 12:18 | 只看该作者

C++ 随笔-22,多态性的引入


多态性(Polymorphism)是初学者最难理解的C++特性之一,“多态性”这个词似乎有点怪异,也许这就是令人难懂的原因吧,其实这个特性完全可以用别的方法描述(如以后将采用的实现机制描述),这样可以更直接的了解其本质。程序员们是一群很怪的家伙,做事情总是采取难理解的方法,有时甚至只是为好玩而使事情搞得很复杂。C语言程序员甚至可以参加一种年度竞赛,谁写的程序最难懂,谁就得一等奖。程序设计语言是通过书、杂志和论文等方式加以推广的,感兴趣的人越多越有利于推广。听起来很怪异的词更可能引起人们的兴趣,促使人们研究语言的新特性。如果有足够多的人发生了兴趣,语言使用者的数量达到了一定程度,该语言就成功了。这就是C++语言的演化过程,也许多态性这个术语在增加语言使用者方面起到了一定的作用。

多态性是指C++的代码可以根据运行情况的不同执行不同的操作。这通常不是由此代码的编写程序员直接控制的,而必须根据此代码的最终衍生情况,依靠C++对象对自身进行跟踪,最终确定相关的操作。与其说多态性是类的特性,不如说它是类成员函数的特性。多态性是通过类的体系结构来实现的,不过只有类的成员函数可以具有多态性,而不是整个类都具有相关特性。下面分几个方面逐步阐述相关的概念和实现。


为何要用多态性

先看一下在封装中所提到的那个状态机的直接包装程序:

enum status_type { S0, S1, S2, S3, S4,…,Sn };
    
class class_SM
{
    protected:
    
    status_type status;
    
    virtual int Fy( int x );
    virtual void Fs( int x );
    
    public:
    
    class_SM( void ) { status = S0; }
    class_SM( status_type S ) { status = S; }
    
    int Do_it( int x );
    
    ~class_SM() {}
};

int class_SM :: Fy( int x )
{
// return Fy( x, status )

    return x;
}

void class_SM :: Fs( int x )
{
// status <- Fs( x, status )
}

int class_SM :: Do_it( int x )
{
    int result;
    
    result = Fy( x ); Fs( x );
    
    return result;
}

现在去掉关键字virtual,采用形式化的描述将程序形式简化如下:

class class_SM
{
    Fy;
    Fs;
    
    Do_it { Fy; Fs; }    
};

其中Fy和Fs是类class_SM中定义的两个成员函数。而Do_it同样也是class_SM的一个成员函数,不同的是其还调用了函数Fy和Fs。在论述继承时,给出了相应的派生类,现在同样用形式化的描述再现如下。

class class_My_SM1 : public class_SM
{
    Fy;
    Fs;
};

其中在类class_My_SM1中,对成员函数Fy和Fs进行了重新定义。由于class_SM是其基类,所以其基类中的成员函数Do_it被原封不动的继承了下来。如果采用前面说过的类成员函数的简单继承和覆盖概念,class_My_SM1虽然自定义了两个新的成员函数class_My_SM1::Fy和class_My_SM1::Fs,但最终却是徒劳。因为其继承来的成员函数Do_it所调用的还是基类的成员函数class_SM::Fy和class_SM::Fs。为了能够使得继承而来的成员函数调用类中新定义的Fy和Fs,就必须引入一种全新的调用机制。为了区别于一般的成员函数的调用,在必须采用特殊调用机制的成员函数前加上一个特殊的关键字——virtual,相关函数被称之为虚函数。采用虚函数后,上面例子的形式化描述再表如下:

class class_SM
{
    virtual Fy;
    virtual Fs;
    
    Do_it { Fy; Fs; }    
};

class class_My_SM1 : public class_SM
{
    virtual Fy;
    virtual Fs;
};

class_My_SM1 My_SM1;

由于引入了多态调用机制,对象My_SM1的成员函数Do_it将调用所属类class_My_SM1中重新定义的虚函数Fy和Fs,这样才真正实现了其功能的更新。

这里要进一步重申的是,虚成员函数(简称虚函数)一般是被类中其他成员函数所调用的。反过来说,如果一个成员函数只是作为顶层函数出现,就没有必要将其归入虚函数之列,虽然在派生类中可能会对它进行覆盖。

使用特权

评论回复
52
short_long| | 2008-4-21 18:00 | 只看该作者

好家伙

听教授讲课

使用特权

评论回复
53
liangyuf| | 2008-4-21 21:31 | 只看该作者

另一种价值!

个人的价值不但了满足自己的需要,更能满足他人的需要,谢谢楼主能跟大家一起分享这么好的东西。谢谢!

使用特权

评论回复
54
HWM|  楼主 | 2008-4-22 10:54 | 只看该作者

C++ 随笔-23,多态性的机理

前面讲了引入多态的必要性,现在从三个方面阐述一下多态性的机理,从中可以窥探到一些本质性的东西。

一,虚函数表

虚函数表(VTAB)是由多态性引入的一个从属于类的线性表。在封装中已经知道,一个类在事例化前已经封装了一堆代码,另外在特殊情况下还包含一些数据表,虚函数表就是其中之一。虚函数表的单元用来存放虚函数的入口地址,具体生成规则如下:

    1) 当发现当前类有虚函数定义,且基类(存在的话)尚未有虚函数表存在,则建立一个空虚函数表并将当前类的所有虚函数入口填入到表中。

    2) 当发现当前类有虚函数定义,且基类已有虚函数表存在,则先将基类的虚函数表复制过来。然后查询表内是否有接口(函数名和参数)完全和本类定义的虚函数接口一致的单元,若有则用本类相关虚函数入口覆盖之,否则就另外增加入虚函数表(添在表末)。

    3) 如果当前类没有新定义的虚函数,若基类(存在的话)已有虚函数表存在,则就将其复制过来。

二,虚表指针

虚表指针(VPTR)是由多态性引入的一个存在于对象(类的事例)内的一个指针。它是在对象事例化过程中由编译器分配给对象的一个数据空间(用于存放VPTR),其内容就是所属类的虚函数表的首地址。

三,调用机制

有了虚函数表和虚表指针就可以阐述虚函数的调用机制了。在论述封装时谈到一个指针this,在此首先要用到它。如果在类的一个成员函数中存在有对某虚函数的一个调用,在具体调用前,要完成如下一些工作:

    1) 由隐形参数this找到当前对象的虚表指针(VPTR)

    2) 再由VPTR找到所属类的虚函数表(VTAB)

    3) 从VTAB中取出所要调用的虚函数入口地址

    4) 调用相应的虚函数

虚函数还可以在非成员函数的环境下被调用。这时候由于必须附加对象名信息,所以由对象名就能直接得到其this指针。其他步骤相同。

下面用一个实例加以说明(注意,同样采用形式化表述):

class class_SM
{
    status;

    virtual Fy;
    virtual Fs;
    
    Do_it { Fy; Fs; }    
};

class class_My_SM1 : public class_SM
{
    virtual Fy;
    virtual Fs;
};

class_My_SM1 My_SM1;

编译器先处理类class_SM,发现有两个虚函数Fy和Fs,便生成了一个VTAB_class_SM。在表内加入Fy和Fs的入口地址Fy_class_SM和Fs_class_SM。然后编译器再处理类class_My_SM1,发现有两个新定义的虚函数Fy和Fs且其基类已存在一个VTAB,便将基类的VTAB复制过来(变成VTAB_class_My_SM1)。在类class_My_SM1的VTAB(VTAB_class_My_SM1)中发现有与本类Fy和Fs具有完全一致接口(函数名和参数)的单元(就是类class_SM中的Fy和Fs),按照规则将当前类的虚函数入口地址Fy_class_My_SM1和Fs_class_My_SM1覆盖在相应的单元上。这样最终形成的两个VTAB为如下:

    VTAB_class_SM:        Fy_class_SM
                          Fs_class_SM

    VTAB_class_My_SM1:    Fy_class_My_SM1
                          Fs_class_My_SM1

有了VTAB,再看一下对象My_SM1中的数据。在My_SM1中除了成员变量status外,还增加了一个指针VPTR,指向VTAB_class_My_SM1。具体形式为:

this  ->  status
          VPTR    ->  VTAB_class_My_SM1: Fy_class_My_SM1
                                         Fs_class_My_SM1

有了上面的“路线图”就不难理解最后的调用机制了。对于对象My_SM1,它的成员函数Do_it中对Fy和Fs的调用过程具体阐述如下:

    由对象My_SM1得到this指针,再由此指针得到对象内的VPTR。由VPTR得到My_SM1所属的类class_My_SM1的VTAB,再由VTAB中的具体虚函数的入口地址调用相关的虚函数。

使用特权

评论回复
55
xushouxue| | 2008-4-22 13:41 | 只看该作者

继续还是一句没看,继续贴!

使用特权

评论回复
56
iC921| | 2008-4-22 16:24 | 只看该作者

主要是方便讨论

一朝成名 发表于 2008-4-18 12:43 新手园地 ←返回版面    

41楼: LZ一页开一帖 

版主无奈就全加裤子

那LZ可以批发裤子了呵呵
 
 

使用特权

评论回复
57
niu9911| | 2008-4-22 23:21 | 只看该作者

那天有空全整理下打出来仔细看

使用特权

评论回复
58
jxb163| | 2008-4-23 08:49 | 只看该作者

ding~~~~~~~~~~~~

使用特权

评论回复
59
HWM|  楼主 | 2008-4-23 09:10 | 只看该作者

C++ 随笔-24,再看一个多态性例子


为了更实际地了解多态性的机理,再给出一个典型的例子,其程序如下:

class A
{
    int x, y, z;

    protected:
    virtual int V1( void ) { return 1; }
    virtual int V2( void ) { return 2; }

    public:
    A( void ) { x = y = z = 0; }
    void Do_it( void ) { x = V1(); y = V2(); }
};

class B : public A
{
    protected:
    virtual int V2( void ) { return 20; }
    virtual int V3( void ) { return 30; }

    public:
    B( void ) : A() {}
    void Do_it( void ) { x = V1(); y = V2(); z = V3(); }
};

class C : public B
{
    protected:
    virtual int V1( void ) { return 100; }
    virtual int V3( void ) { return 300; }

    public:
    C( void ) : B() {}
};

A a;
B b;
C c;

int main( void )
{
    a.Do_it(); // a.x <- 1, a.y <- 2
    b.Do_it(); // b.x <- 1, b.y <- 20, b.z <- 30
    c.Do_it(); // c.x <- 100, c.y <- 20, c.z <- 300

    while ( 1 )
    {
    }
}


类A,B和C的虚函数表分别是:

        VTAB_A: V1_A ( return 1 )
                V2_A ( return 2 )

        VTAB_B: V1_A ( return 1 )
                V2_B ( return 20 )
                V3_B ( return 30 )

        VTAB_C: V1_C ( return 100 )
                V2_B ( return 20 )
                V3_C ( return 300 )

而对象a,b和c的数据单元分布分别是:

        this_a  ->  a.x
                    a.y
                    a.z
                    a.VPTR (->VTAB_A)

        this_b  ->  b.x
                    b.y
                    b.z
                    b.VPTR (->VTAB_B)

        this_c  ->  c.x
                    c.y
                    c.z
                    c.VPTR (->VTAB_C)

根据多态性的调用机制,很方便的可以得出上面程序的执行结果是:

    a.x <- 1
    a.y <- 2
    a.z <- 0 ( 初始化为零 )

    b.x <- 1
    b.y <- 20
    b.z <- 30

    c.x <- 100
    c.y <- 20
    c.z <- 300

使用特权

评论回复
60
wnch| | 2008-4-24 16:04 | 只看该作者

留下脚印,回头再看

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则