分享
 
 
 

C++ Advanced Training(一)

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

C++ Advanced Training(一)

-------C++&ADT

作为一名刚来公司不久的新员工,有幸参加由侯捷老师做的高级C++培训,真的是很高兴。从接触Programming以来,C++一直是自己的主打语言(虽然最近正在研究Java^_^)。一天的培训下来,收获还是蛮大的,侯老师的细致入微的讲解给我留下了很深的印象。

将我所得到的东西与大家分享同时对我来说又是个复习的过程,不失为一箭双雕之举^_^。

这次的高级C++培训分3个部分,分别为C++&ADT(一个独立Class的设计经验),C++&OOP,OOD&Patterns,我将用几篇文章来写我的收获,由于我的水平也有限,特别是对OOD和Patterns的理解不是很透彻,所以难免会出现不正确的“论点”还希望大家多多指点。

一、侯老师的开场白

侯老师有一部很有名的著作叫“无责任书评”(在侯老师的acer笔记本(我的本本也是acer的^_^)中,壁纸就是这本书的封面),在业界很受欢迎。侯老师自己是技术作家也出版各类的技术书籍,所以对书有着他独到的见解。由于我们是高级C++培训,所以这次侯老师主要向我们介绍了所谓的C++大系的书籍,简单列举如下:(按侯捷的分类方式)

² 百科全书:《Thinking in C++》、《C++ Primer》、《The C++ Programming language 》、《The C++ Standard Library》

² 专家经验:《Effective C++》、《More Effective C++》、《Exceptional C++》、《More Exceptional C++》

² 底层内幕:《多态与虚拟》、《深入探索C++对象模型》

² 设计相关:《Design Pattern》、《Large Scale C++ Design》

二、正文(主题)

1、 从C的角度看object-based programming(基于对象编程)

侯老师从C开始我觉得还是很合适的,一是培训的对象大多使用C做开发(这也与我们公司的业务领域息息相关,老的C程序员也许不太喜欢转移到C++),二是这样做可以间接的把C++编译器的实现方法展现在我们面前,便于对C++作深入理解。

用C提供的语法简单模拟C++类的概念。

分析C能给我们提供什么:

² C的struct能对data进行封装;

² C的struct可使用function pointer来模拟member function

C不能给我们提供什么:

² C的struct不能提供对access level的支持。

这里我也举个例子(不是侯老师讲义中的例子)来模拟C++。

#include <stdio.h>

#define MANAGER 0

#define CLERK 1

#define MEWER 2

typedef struct tagPerson

{

int age;

int baseSalary;

int jobType;

int (*calcSalary)(struct tagPerson*);

void (*printInfo)(struct tagPerson*);

}Person;

int calcSalary(Person* this)

{

if(this->jobType == MANAGER)

return this->baseSalary*10;

else if(this->jobType == CLERK)

return this->baseSalary*5;

else

return this->baseSalary;

}

void printInfo(Person* this)

{

//print the person's info include jobType,salary and so on;

printf("the person's age is %d \n" , this->age);

}

int main()

{

Person aPerson={24,1000,MANAGER,&calcSalary,&printInfo};

Person bPerson={23,1000,CLERK,&calcSalary,&printInfo};

int salary = aPerson.calcSalary(&aPerson);

printf("the aperson's salary is %d\n" , salary);

aPerson.printInfo(&aPerson);

salary = bPerson.calcSalary(&bPerson);

printf("the bperson's salary is %d\n" , salary);

aPerson.printInfo(&bPerson);

}

output:

the person's salary is 10000

the person's age is 24

the bperson's salary is 5000

the person's age is 23

值得大家注意的地方我都用红色加粗的字体了,在上面程序中我们先将Person的实例化为对象aPerson和bPerson,并手工将他们的地址传给他们自己的函数指针成员来模拟C++的成员函数的调用。现实中C++编译器将我们这一手工过程自动化了并隐藏了起来,也就是C++在每个成员函数的参数类表中偷偷添加了该对象的地址指针this。看起来也挺好理解的,但是这仅仅是C对C++简单的模拟,C++所提供的强大的面向对象的特性是C所不能比拟的,其实归根结底来说还是思维方式的转变带来的巨大变化。

2、 建议使用最新的标准C++ style

C++从诞生那天到现在已经有20多年了,这期间C++程序的style也经历几次大的变化,直至今日,我们提倡采用C++标准程式库的code style,无论是初学者还是老手,都应该这么做,与标准靠拢是最好的选择。这里不详细阐述了,C++的creator的主页上就有很详尽的说明该如何写Standard C++代码,更有其观点“treat the standard c++ as a new language”。这里仅举几个简单观点和例子:

² standard header files #include <cstdio> ß#include <stdio.h>

#include <iostream> ß#include <iostream.h>

² namespace std

² try to use stardard library as possible as you can!

3、 forward declaration

以前一直对forward declaration不是很理解,今天终于有所突破了,所以就写下来,希望对那些和我有同样困惑的朋友们有所启发和帮助。

看下面的两段代码:

代码段1:

class A; //forward declaration

class B

{

//....

A* a1;

A* a2;

};

代码段2:

class A; //forward declaration

class B

{

//....

A a1;

A a2;

};

直接告诉大家结论:代码段1顺利通过编译;代码段2则编译失败。

原因分析:

代码段1:由于B中的两个数据成员都是A*指针类型,指针类型在32位平台上大小都是4byte,编译器无需知道A的具体大小。那为什么还要有class A; //forward declaration这行代码呢,是因为编译器要知道代码中是否存在A这个类型,这行代码就是告诉编译器“你放心编译吧,这个A类型存在”。而代码段2的B中的两个数据成员都是A类型,编译器必须知道A的具体大小,仅仅告诉编译器A类型的存在是远远不够的。

4、 function signature &function prototype(注意有特例)

Function prototype即是函数在声明时的所有元素的集合,包括函数名字,返回类型,参数列表。

Funcition signature则是function prototype去掉返回类型后的剩余部分。

对于这两个概念我们还是举例说明比较直观,看下面的例子:

Function prototype:double calcSalary(Person* person);

Function signature:clacSalary(Person* person);

是否能够很好的区分这两个概念会直接关系到你对成员函数overloading的理解。

牢记Overloading关注的是function signature 而不是function prototype!

但是这里有个特例,那就是“pass by value和pass by reference是不同的signature么?”答案:不是。我们也可以举例说明这点,看下面的例子。

class A

{

Public:

int getArea(Circle& cir);

int getArea(Circle cir);

};

main()

{

Circle aCir;

A a;

a.getArea(aCir);//Ambiguous

}

某些编译器在class A的编译时并不报错,但是在真正调用时,发生模棱两可。

还有一个问题就是“为什么overloading不关心返回值类型呢”

我们还是举例说明一下:

class A

{

Public:

int PrintInfo(Person& a);

void PrintInfo(Person& a);

};

main()

{

Person aPerson;

A a;

a.PrintInfo(aPerson); //Ambiguous

}

说明:有些时候我们并不关心返回值,就像上例中的代码。一旦返回值类型可以作为overloading的一个评判依据,某些时候会造成模棱两可的错误。

5、 尽量以const和inline替换#define(即macro)

这是个老话题,又是一个大家都容易犯的问题,这里就允许我再提一次吧^_^

使用macro无非两个用处

1)、定义常量

2)、实现简单的函数功能

使用macro定义常量的缺点:

由于宏是由precompiler处理的,所以在真正的compiler处理之前就被precompiler移走了,没能进入符号表(symbol table),所以导致调试时的困难。

例:#define PI 3.1415926

我们可以用const double PI = 3.1415926替代。

还有一种class专有常数的例子:

class GamePlayer

{

static const int NUM_TURNS = 5;

int scores[NUM_TURNS];

...

};

Const int GamePlayer::NUM_TURNS;

注意:in-class initialization只对整数类型(int ,bool,chars等)才成立且对常数才成立。

使用macro模拟函数功能的缺点:

例: #define max(a , b) ((a) > (b)) ? (a): (b))

int a = 5 ,b =0;

max(++a , b); //我们期望是6>0,可实际结果是7>0,因为a被累加了两次。

我们的替代方法:inline int max(int a , int b){return a>b ? a : b ;}

Inline function可以对参数进行类型检查,而且拥有和macro一样的效率。

6、 by reference vs by value

这里有几个原则(当然每个原则都不是强制性的,也都有特例),我们逐条来理解吧:

1)尽量使用by reference,不要使用by value,无论是传入参数还是传回返回值。

² reference通常不用于变量的修饰,多用于参数传递和返回值的修饰。

² by reference既有by pointer的效率,又保持接口与by value时不变,这些都是by reference的优点所在。

² 如果一定要by value,也不要钻牛角尖非得用by reference不可。(在下面一条的例子中就有体现)

2)不要在函式中传回local object的reference。(会造成dangling”空悬”问题)

例:Complex& func(...)

{

Complex c;

//...

return c;

}

说明:这个函数会造成reference’s dangling problem,因为函数原型定义要传回reference,而代码却传回一个local object’s ref。

解决办法:修改返回值类型为by value

Complex func(...)

{

Complex c;

//...

return c;

}

这样就一切ok了。(从函数代码的return c还看不出返回值是by value还是by

reference, 得看函数原型的声明格式,如果是Complex& 则是by reference)。

7、 const object与const member function

我们看一个类的成员函数的原型:

class Test

{

//...

const A& function(const B& b)const;

};

相信有很多人看完上述的函数原型后都有些“晕”,那么多const,都起什么作用亚。我们来一一讲解吧。

返回值类型:const A& ------ 表示返回的值为常量,一般不能够作为左值,不能被修改(可作为右值)。

参数类型:const B& ------- 表示传入的参数b在函数的执行过程中状态应保持不变,不被修改。

函数后的修饰符const ------ 表示该成员函数的执行不会改变类的状态,也就是说不会修改类的数据成员。

所谓的const object即在实例化时前面有const关键字修饰。

如const A a;

所谓的const member function是指函数后有修饰符const,其通用的格式为:

return_type fun_name(parameters list)const;

这样就会涉及到non-const object , const object 是否能够调用 non-const member function, const member function的问题,他们之间的调用关系我们可用下面的表格描述:

const object

non-const object

const member function

ok

ok

non-const member function

err

ok

8、 friend & operator <<

有这样一段代码:

Complex c(3,5);

cout << c <<endl;

要想上一段代码编译和运行正常,我们应该做些什么呢?

在Complex类中重载<<符号,现在就有两种选择,是选择member func版还是non-member func版呢,根据上一段代码,cout << c,如果选择member func版,则书写方法应该是c<<cout,这明显不符合C++的习惯。所以我们选择non-member func版。版本选完后,我们来定这个重载函数的prototype, 由于cout为ostream类型且考虑到cout <<c<<c1这种级联形式,我们决定基本的原型如下:

osteam& operator<< (ostream& os , const Complex& r);

由于该函数需要访问Complex的private data member,所以我们再在前面加上一个friend关键字,完整的声明如下:

Class Complex

{

//...

friend osteam& operator<< (ostream& os , const Complex& r);

}

有了friend关键字表示Complex类告诉编译器operator<<函数是朋友,可以访问private data member。另外注意friend不具备传递性。

9、member func接受同型的obj,有无权利access private data member

我们还是看例子:

Complex& Complex::operator+= (const Complex& x)

{

m_real += x.m_real;

m_imag+= x.m_imag;

return *this;

}

从例子中看到member func接受同型的obj,是有权利access private data member的。

10、指针使用的好的编码习惯

当动态分配内存时,指针的使用应该格外的小心,一不注意就会造成memory leak。

好的指针使用习惯会帮助你减少这种情况的发生。下面举例说明:

A p = new A();

当要free 这块内存时,应如下作法:

delete p;

p = null;//将p赋值为null以防止别人在free内存后,继续使用这个指针。

当使用p时,应如下作:

if(p)//做一次判断,如果p不等于null,就可以使用了。

{

p->dosomething();

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有