MFC第三课——C++特性

首先感谢候俊杰先生与《深入浅出MFC》(第二版),以下心得都来自于对该书的学习~

一、封装(member variable成员变量、member function成员函数)

    把数据声明为private,不允许外界随意存取,只能通过特定的接口来操作,这就是面向对象的封装(encapsulation)特性。

 

二、基类与派生类——继承(Inheritance)

    问题:基类的函数如何处理派生类对应的不同的数据?答案在于传入的参数——this指针。(这是一个隐藏参数)

 

三、虚函数与多态(Polymorphism)

    要调用父类的函数,你必须使用scope resolution operator(::)明白指出。

    结论:

    ①如果以一个"基类指针"指向一个"派生类对象",那么经由此指针,你就只能够调用基类所定义的函数。

    ②如果你以一个"派生类指针"指向一个"基类对象",你必须先做明显的转型操作(explict cast),但不提倡。

    ③如果基类和派生类都定义了"相同名称的成员函数",那么通过对象指针调用成员函数时,到底调用到哪个函数,必须视该指针的原始类型而定。

    多态靠虚函数完成。虚函数是对①这条规则反其道而行的设计!

    如果你预期派生类有可能重新定义某个成员函数,那么你就在基类中把此函数设为virtual。

    MFC有两个十分十分重要的虚函数:与document有关的Serialize函数和与view有关的OnDraw函数,你应该在自己的CMyDoc和CMyView中改写这两个虚函数。

    对于多态,编译器无法在编译时期判断到底是调用哪一个函数,必须在执行期才能判断,这称为后期绑定late binding或动态绑定dynamic binding。至于C函数或C++的non-virtual函数,在编译时期就转换为一个固定地址的调用了,这称为前期绑定early binding或静态绑定static binding。

 

    纯虚函数"=0"

    纯虚函数不需要定义其实际操作,它的存在只是为了在派生类中被重新定义,只是为了提供一个多态接口。只要是拥有纯虚函数的类,就是一种抽象类,它是不能够被实例化(instantiate)的,也就是说,你不能根据它产生一个对象。另外,若派生类继承了基类后没有改写基类的纯虚函数,那么该派生类本身也就成了拥有纯虚函数的类,即为抽象类。

    关于虚函数和多态,还有几点需要说明:

    ①如果你期望派生类重新定义一个成员函数,那么你应该在基类中把此函数设为virtual。

    ②抽象类不能产生出对象实例,但是我们可以拥有指向抽象类的指针,以便于操作抽象类的各种派生类。

    ③虚函数派生下去仍为虚函数,而且可以省略virtual关键词。

 

四、虚函数表(vtable)

    每个"内含虚函数的类",编译器都会为它做出一个虚函数表,表中的每一个元素都指向一个虚函数的地址。此外,编译器当然也会为类加上一项成员变量,是一个指向该虚函数表的指针(常被称为vptr)。(关于C++类的成员函数,你并没有在Class对象的内存区块中看到与成员函数有关的任何东西。)

    虚函数表的内容是依据类中的虚函数声明次序,一一填入函数指针。

    派生类会继承基类的虚函数表(以及所有其它可以继承的成员)。当我们在派生类中改写虚函数时,虚函数表就受了影响:表中元素所指的函数地址将不再是基类的函数地址,而是派生类的函数地址。

 

五、静态成员(变量与函数)

    不要把static成员变量的初始化操作安排在类的构造函数中,因为构造函数可能一再被调用,而变量的初值却只应该设定一次。也不要把初始化操作安排在头文件中,因为它可能会被载入许多地方,因此也就可能被执行许多次。

    设定static成员变量初值时,不受任何存取权限的束缚。

    由于static成员函数不需要借助任何对象,就可以被调用执行,所以编译器不会为它暗加一个this指针。也因此,static成员函数无法处理类之中的non-static成员变量。

 

六、构造函数与析构函数

    C++的new运算子和C的malloc函数都是用于配置内存,但前者比之后者的优点是,new不但配置对象所需的内存空间,同时会引发构造函数的执行。

    一个有着层次结构的类群组,当派生类的对象诞生之时,构造函数的执行是由最基类(most based)至最尾端派生类(most derived);当对象要毁灭之前,析构函数的执行则是反其道而行。

    在C++中,有四种方法可以产生一个对象:

    ①在堆栈(stack)之中产生;

    ②在堆(heap)中产生;

    ③产生一个全局对象(同时也必然是个静态对象);

    ④产生一个局部静态对象。

    当编译器编译你的程序,发现一个静态对象时,它会把这个对象加到一个链表中。更精确的说法是,编译器不只是加上此静态对象,它还加上一个指针,指针对象之构造函数及其参数。把控制权交给程序进入点(main或WinMain)之前,startup代码会快速在该链表上移动,调用所有登记在案的构造函数并使用登记在案的参数,于是就初始化了你的静态对象。

 

七、运行时类型识别(RTTI)

    Runtime Type Information

 

八、异常处理(Exception Handling)

    C++的exception基本上是与C的setjmp和longjmp函数对等的东西,但它增加了一些功能,以处理C++程序的特别需求。从多层嵌套的例程调用中直接以一条快捷方式撤回到异常情况处理例程(exception handler),这种"错误管理方式"远比结构化程序中经过层层的例程传回一系列的错误状态来得好。

    C++导入了三个新的exception保留字:try、catch、throw

 

九、Template

    C++的template有两种:一种针对function,另一种针对class。

    template函数的数据类型参数T几乎可以适应"任何数据类型",但函数中对该类型数值的任何运算操作,都必须支持。

    Templates的编译与链接:

    编译器遇到一个template时,不能够立刻为它产生机器代码,它必须等待,直到template被指定某种类型。

    有一个常用的技术,Borland称之为Smart(在链接过程中,所有重复的部分将被删除),应该算是最容易的:每一个使用Template的程序代码的目的文件中都存在有template代码,链接器负责复制和删除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值