分享
 
 
 

C++ Advanced Training(二)

王朝c/c++·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

C++ Advanced Training(二)

-------C++&OOP

今天侯老师花了2个小节的时间把昨天的“尾巴”讲完,然后就进入今天的正题OOP,注意是OOP,not OOD。

听了侯老师的两天课,感觉他的讲课风格是

² 关注细节

² 以讲”故事”的方式来讲解抽象的技术。

我将继续接上一节的内容谈C++。

1、Increment operator(++)

++ operator分为 ++A 和A++两种,实际在实现中A++调用了++A。我们举个例子

class Fraction

{

Fraction& operator++();

Fraction& operator++(int);

}

inline Fraction& operator++()

{

m_numerator += m_denominator;

return *this;

}

inline Fraction& operator++(int)

{

Fraction oldValue = *this;

++(*this); // call the prefix increment

return oldValue; //why?

}

从以上的代码段中我们可以得到两个结论:

1)从代码可以看出在使用++ operator时,特别是对自定义类型的++时,尽量选用++A型,因为A++在实现中实际上是调用A++,所以A++型要比++A型执行速度慢。

2)我们在设计数值型class时,最好以int为参照物。这也是为什么Fraction& operator++(int)返回oldValue的原因。我们举例说明在使用primitive type int时,++的用法:

int a = 5;

int b = a++;

cout << a << endl; // a = 6

cout << b << endl; // b = 5

可见A++型,是先返回A的值,再做++操作。所以我们在自定义数值型class的时候也要模拟这种方式,使++ operator的使用方式保持一致,无论对primitive type 还是user-defined type。

2、scope and lifetime

这里总结以下各种object的lifetime:

² global object program始 ,program终

² local(auto) object scope始 , scope终

² heap(dynamic allocated ) object new始 , delete终

² static local object scope始 ,program终

说明:

1) global object的建构是在main之前所以利用global object的ctor可以帮助你做一些有用的事,MFC就利用了这点完成了许多有用的操作。

2) 在program终止之前(即在main函数执行结束之前),有global object , static local object at somewhere 和local object in main等的dtor会被调用。但是次序不定(视编译器实作方式而定),下面代码列出VC++7.1的做法:

#include <iostream>

#include <string>

using namespace std;

class Test2

{

public:

Test2(const string& str) : m_name(str)

{

cout << "constructor called for " << m_name << endl;

}

~Test2()

{

cout << "destructor called for " << m_name << endl;

}

private:

string m_name;

};

void g_func()

{

static Test2 l_TestObj1("StaticLocalObjInGlobalFunc");

}

Test2 g_TestObj("GlobalObj");

int main(int argc, char *argv[])

{

Test2 l_TestObj2("LocalObjInMain");

g_func();

return 0;

}

Output:

constructor called for GlobalObj

constructor called for LocalObjInMain

constructor called for StaticLocalObjInGlobalFunc

destructor called for LocalObjInMain

destructor called for StaticLocalObjInGlobalFunc

destructor called for GlobalObj

3、static member

1)static data members

² 独立于objects之外,众多objects共享一份static data members,也就是说每个class只有一份;

² static data members可被继承(其access level)。

2)static member function 特点

² 没有this pointer ,因此就像non-member function一样;

² 必定不为virtual;

² 可以不通过object而直接访问(通过类的全名,如Accout::setRate())。

3)static member function 用途

² 用于处理static data member;

² 用于callback function。

static member function用于处理static data member无可厚非,我们也不必细讲,关键是为什么使用static member function来用于callback,为什么不直接是用non-static member function?

首先我们要知道什么是callback function?callback function是如何运行的?

callback中文译为“回调”,台湾译为“回呼”,我们拿一个实际的例子来解释什么是callback , callback function是如何工作的?

在Window平台上开发GUI应用程序时,我们会常常用到一个Win32 API,其原型如下:

BOOL LineDDA(

int nXStart, // x-coordinate of line's starting point

int nYStart, // y-coordinate of line's starting point

int nXEnd, // x-coordinate of line's ending point

int nYEnd, // y-coordinate of line's ending point

LINEDDAPROC lpLineFunc, // pointer to callback function

LPARAM lpData // pointer to application-defined data

);

这个函数的用途在msdn中被描述为 “The LineDDA function determines which pixels should be highlighted for a line defined by the specified starting and ending points. ”这个函数是做什么的我们不关心,我们关心的是它的第5个参数,这是一个LINEDDAPROC类型的函数指针,也就是说我们要使用LineDDA这个函数就必须传入一个函数地址,这是因为LineDDA在执行过程中有些动作不能确定,需要我们来告诉它怎么做,我们如何告诉它呢,就通过传入这个有着固定signature的函数的地址,而这个被LineDDA所使用的函数就叫做callback function。下面用一个图形表示这个过程:

通过上面的图示我们可以看出:

callback function的signature是事先定义好的,包括参数的类型和个数等。

下面我们我们就利用这个来解释为什么non-static member function不能作为callback function了。我们都知道一个class的non-static member function在被调用时,编译器会将this这一隐藏的指针加入到该funtion的参数列表中去,导致参数的个数增加而不符合callback预先定义好的signature。而static member function不含有this这一隐藏指针,所以完全胜任callback function这一角色。

4)static member function、non-static member function 、static data member和non-static data member之间的关系

告诉大家一个总的原则,理解上述几个member关系的关键在于this指针。

具体地说:

non-static member function既可以调用static member function,也可以处理static data member;

static member function则既不能调用non-static member function,也不能处理non-static data member。

4、new expression(new operator)&operator new

new operator和operator new这两个东西让一些初学者感到不能理解,甚至包括一些用过很长时间C++的老手都很可能被迷惑,这两个到底有什么区别?各自代表什么意思呢?

我们举个例子大家就清楚了。

Complex* pc = new Complex(1,2); //这句代码里的new就是new operator,它是C++ 的一个关键字,当这条语句执行时,编译器会执行一系列动作。依次为:

² 调用::operator new分配内存空间;

² casting(转型)

² invoke Complex的constuctor

其中第一步调用::operator new分配内存空间中的::operator new就是我们所说的后者,它是真正分配内存的执行者,相当于C中的malloc函数,与malloc不同的是::operator new可以被重新定义,你可以定义你自己class专用的operator new函数。为什么我们要这么做呢?因为使用默认的::operator new分配每一块内存的同时也会分配一块叫cookie的内存块用来存放一些帮助内存管理的信息,如分配的内存的大小,供delete使用。在一些embeded system中,memory是limited的。我们要尽量减少cookie的分配,所以我们要定义自己的operator new。比如我们可以事先分配一大块内存,以后再需要动态分配内存时,就在这个大块内存中再分配出来既可。

operator new 在对象产生之前被调用,所以必须是static的。(同理,operator delete在对象被销毁后被调用,也应该是static的),一般即使你不explicit的声明为static的,编译器也会自动默认为static的。

5、delete expression(delete operator)&operator delete

有了4中的new operator&operator new的基础,这节的东西就很好理解了。

delete pc;

编译器会执行一系列动作,依次是:

² invoke Complex的destructor;

² 调用::operator delete释放内存空间。

::operator delete 等价于C的free函数。

::operator delete和::operator new类似也可以被重新定义你自己的版本。

下面举个例子(包含operator new 和operator delete)

class Base

{

public:

static void* operator new(size_t size);

static void operator delete(void* rawMemory , size_t size);

};

void* Base::operator new(size_t size)

{

if(size != sizeof(Base)) //大小错误,可能是被子类调用

return ::operator new(size);//交给默认处理函数处理

else

//your code to alloc the memory

}

void Base::operator deletevoid* rawMemory , size_t size)

{

if(rawMemory == 0) return;

if(size != sizeof(Base)) //大小错误,可能是被子类调用

{

::operator delete(rawMemory);//交给默认处理函数处理

return;

}

else

{

//your code to free the memory

}

}

main()

{

Base *p = new Base(); //call the operator new which you defined

delete p; // call the operator delete which you defined

}

main代码中当编译器扫描到new时会看Base类中是否重新定义了operator new,如果是则调用Base专用的operator new。delete也是同理。

注:关于operator new &operator delete的一个原则就是:如果你写了一个operator new,就应该写一个对应的operator delete

开始OOP

侯老师认为学好OOP就要学好两方面:polymorphism和template method。

我的一个同事一直和侯老师争论下面的这两个概念的理解,这里我把我的理解写下来:

framework &application framework

framework---- it is always a library which is large ,complex and have many classes and many associations among these classes. such as c++ library , Microsoft .net class library,Win32 API

application framework---- it have helped you define the skeleton of the application ,what you should do is only to override some virtual functions or add some business logic code , that is all。such as MFC ,VCL等。

6、SubObject and virtual destructor

我们看一个例子来说明subobject的概念和virtual destructor的用途。

大家从上面的图中也会有所了解subobject的概念。在CSquare object中,既有CRect的suboject又有CShape的subobject。它们的构造顺序是:由内向外,而析构顺序为:由外向内。

如果有下面代码:

CRect* p = new CSquare();

delete p;

这时如果CRect的dtor为non-virtual的,上述的代码就相当于企图用一个拥有non-virtual dtor的base class的指针来删除一个derived class oject, 其结果是未定义的。最可能的是执行期未调用derived object的dtor, 因为compiler看到基类拥有的是non-virtual dtor,所以根据p的静态类型将dtor编死,而不经过虚拟机制的route。所以告诫如下:“总是让base class拥有virtual dtor”。这样通过虚拟机制route的编译会将derived类的dtor编进去,我们就能够通过基类指针销毁derived object了。

7、Template method

其实这是design pattern的内容,由于这个pattern比较好理解,所以侯老师把它拿到前面来了。我们还是画图理解比较容易,

侯老师说理解这个关键在于理解library code(你用money买的) 和application code(你自己写的),心中在这两个code之间划一条线(见图中那条虚线),库代码都是固定的,不会因为你的业务逻辑而改变的。在库代码中一般都存在这样的函数,它的动作流程很规律,比如Windows应用程序的打开文件操作,流程不过是“打开文件对话框”、“选择文件类型和文件名”、“读入文件内容”等,无论事打开什么文件这个流程都不会改变,这类函数被称为template method。还是以打开文件这一动作为例,在该template method中我们要有一个函数负责读取文件的内容,而文件的类型多种多样,内容的格式也不相同,那我们如何在代码执行到这个读取文件函数(primitiveFunc)时能根据不同的文件类型执行不同的动作呢?我们利用polyphorism机制,见上面的图形,当main中的代码执行到a.TemplateMethod中的primitiveFunc的时候,代码将调用不同的子类override的那个primitiveFunc而不是库代码中实现的那个primitiveFunc。

8、Polymorphism vs static type &dynamic type

我个人认为学好polymorphism的关键在于:

1) 看call through object 还是 call through pointer

2) static type or dynamic type

至于什么是多态,我这里就不多说了,任何一本C++教材都会有详细的讲解。

static type ---- 变量声明时的type;

dynamic type ---- 变量实际的type;

举例说明:

CShape* p ;

p = new CRect();

上述代码中指针p的static type为CShape* , 而dynamic type为CRect* 。

再看看下面代码:

class CShape

{

public:

virtual void draw()

{

cout << "Draw for CShape" << endl;

}

};

class CRect : public CShape

{

public:

virtual void draw()

{

cout << "Draw for CRect" << endl;

}

};

class CSquare : public CRect

{

public:

virtual void draw()

{

cout << "Draw for CSquare" << endl;

}

};

int main(int argc, char *argv[])

{

CShape* p;

CShape s;

s.draw(); //invoke CShape::draw()

CRect rc1;

rc1.draw(); //invoke CRect::draw()

p = new CRect();

p->draw(); //invoke CRect::draw()

delete p;

p = new CSquare();

p->draw(); //invoke CSquare::draw()

delete p;

return 0;

}

Output:

Draw for CShape

Draw for CRect

Draw for CRect

Draw for CSquare

通过pointer去call function时,编译器会去查看该pointer的动态类型来决定到底调用哪个函数。如上述代码中的指针p,第一次被赋予一个CRect* 类型,通过p call draw时,compiler得知p的dynamic type为CRect* ,而不是CShape*,所以调用CRect::draw;同理第二次调用的是动态类型CSquare的draw。

通过obj调用function时比较简单,obj是什么类型的就调用哪个类型的draw即可。

9、Inside the object model

这里涉及到virtual pointer、virtual table等而且要画大量的图才能理解的更好,我倒觉得不如看看inside the c++ object model这本书,所以这里就不详细描述了(^_^其实我比较懒)。

10、virtual func vs non-virtual func vs pure virtual func

pure virtual func --- 为了让derived class只继承其接口。

virtual virtual func --- 为了让derived class继承该函数的接口和预设行为。

non-virtual func --- 为了让derived class继承该函数的接口和实现(继承实现的前提是derived class没有hide该函数接口)。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有