`
kofsky
  • 浏览: 195777 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

讨论记录之C++细节

阅读更多

ParticipantsLFHZPCPPZY<!----><o:p></o:p>

Date<!----><st1:chsdate isrocdate="False" month="9" islunardate="False" day="16" w:st="on" year="2008">08-09-16</st1:chsdate>  7:20PM<o:p></o:p>

Recorder: CPPZY<o:p></o:p>

参考文献:<o:p></o:p>

1、《effective C++2nd EditionScott Meyers etc.<o:p></o:p>

2、《C++程序设计教程》,钱能<o:p></o:p>

3、《高质量C++C编程指南》,林锐<o:p></o:p>

4http://keith.ecjtu.com/article.asp?id=319<o:p></o:p>

<o:p> </o:p>应大家的要求,今天晚上开始了我们的第一次讨论会。<o:p></o:p>

主要是针对C++里面的一小撮问题展开的,这里我给出讨论的概要:<o:p></o:p>

1、关于优先级与结合性;(这里要重点“批”一下HZPZY,正号和加号都分不清的家伙)(顶)<o:p></o:p>

2#define \inline\const(顺便涉及到inline virtual的连用问题);<o:p></o:p>

3const的作用(包括修饰类的成员函数,成员函数返回值,成员函数的参数列表,数据成员);<o:p></o:p>

4、重载、覆盖(重写/改写,实现多态)以及隐藏的区别;<o:p></o:p>

5、构造函数和析构函数<o:p></o:p>

       6、关于虚拟函数、虚基类及多继承<o:p></o:p>

1、优先级口诀:(除了标明是右结合外,都是左结合)<o:p></o:p>

括号成员第一;//[]、()<o:p></o:p>

全体单目第二;//比如++--+(正号)、-(负号)、指针运算符* & 右结合<o:p></o:p>

乘除余第三;//取余 左结合<o:p></o:p>

移位五,关系六;<o:p></o:p>

等于不等排第七;<o:p></o:p>

位与亦或和位或;//& ^|<o:p></o:p>

逻辑或跟与;//&&||<o:p></o:p>

条件高于赋值;//注意的是赋值运算符很多,包括= *= /= += -= |= <<=>>=  二者都是右结合<o:p></o:p>

逗号排最后。<o:p></o:p>

上面是C中的规则,而C++由于引入了一些新的运算符,因此,有些出入,如表1

 

<st1:chmetcnv tcsc="0" hasspace="True" sourcevalue="1" numbertype="1" negative="False" unitname="C" w:st="on">1 C</st1:chmetcnv>++ 运算符优先级列表<o:p></o:p>

见两个例子:<o:p></o:p>

(1) int x=1y=0<o:p></o:p>

x&&x+y&&++y<o:p></o:p>

加括号确定优先级的方法
  当多个优先级不同的运算符在一起时,为了不混淆,可以先加上括号,这样就分出层次了,相同层次的考虑结合性问题,当确定下来先算那块时,再往这块里面深入。例如上面的例子,我们可以这样加上括号:从左向右看,由于!比&&优先级高,所以有(!x),又由于&&+优先级低,所以有(x+y),而++优先级高于&&,所以(++y)。这样整个式子就变成了:(!x&&x+y&&++y),最外层的是两个&&运算,由于&&的结合性是从左至右,所以上式可看成:A&&B&&C,先计算A,再计算B,最后算C.由于x=1,则!x就为假,后面的就不需要再算了,整个语句的值为假。执行完后,y的值没变,还是0.
  所以碰到不清楚先算谁后算谁时,先加个括号看看,就明白了先后次序。<o:p></o:p>

(2)给语句c=a>bab;加括号。此语句有三个运算符:=>、? :,应该怎样加括号呢?<o:p></o:p>

 第一种方案:c=((a>b)?ab);
  第二种方案:c=a>bab));
  第三种方案:(c=a>bab);
  应该是那一种呢?按照运算符优先级的高低顺序,>优先级高于=,所以不可能把(c=a)括起来。而>优先级高于? :运算符。所以也不可能把(bab)括起来。因此,第一种答案正确。<o:p></o:p>

<o:p> </o:p>

2、尽量以constinline取代#define<o:p></o:p>

尽量以编译器取代预处理器或许更好,因为#define通常不被视为语言本身的一部分。<o:p></o:p>

#define导致的结果就是程序内所使用的名称并未出现于符号表之中。可以改用常量来声明。<o:p></o:p>

若是需要一个class专属常量,即将这个常量的scope局限于class 之内,必须让它成为一个member,而为了确保这个常量至多只有一份实体,则必须让他成为一个static member,例如:
class GamePlayer{<o:p></o:p>

private:
       static const int NUM;//
仅仅是个声明而非定义<o:p></o:p>

    …<o:p></o:p>

}<o:p></o:p>

必须在类定义文件中定义该类成员:const int GamePlayer::NUM=5;<o:p></o:p>

     另一个误用#define指令的常见例子是,以它来实现宏——看起来像函数,却又不会带来函数调用所需的成本。经典例子就是计算两数的最大值:<o:p></o:p>

#define  max(a,b)  ((a)>(b)?(a) : (b))<o:p></o:p>

即使加上了小括号,还是会发生一个可怕的动作:<o:p></o:p>

int a=5,b=0;<o:p></o:p>

max(++a,b);//a被累加两次<o:p></o:p>

max(++a,b+10);//a被累加一次<o:p></o:p>

这时,我们可以使用inline函数,既可以得到宏带来的高效率以及函数带来的可预期行为和类型检验。例如:<o:p></o:p>

inline int max(int a, int b) {return a>b?a:b; }<o:p></o:p>

这与前述宏并不完全相同,因为这个版本的max只接受int类型参数,不过,template可以修正这一问题,这里by reference相比by value可以获取更高的效率。<o:p></o:p>

template<class T><o:p></o:p>

inline const T& max(const T& a, const T&b)<o:p></o:p>

{return a>b?a:b; }<o:p></o:p>

 

3const的作用<o:p></o:p>

1)修饰类的成员函数时:<o:p></o:p>

即在成员函数声明时将const置于成员函数参数列表后分号前,代表它不能对类的数据成员进行修改,但是有一个例外,就是当数据成员前有mutable修饰时,它是可以被该函数修改的;<o:p></o:p>

2)修饰成员函数返回值及函数参数时:<o:p></o:p>

意即被修饰的量是不可被修改的,前者意味着在成员函数返回后得到的值不可被更改,后者意味着不能在函数体内对参数进行变动,只能读取它;<o:p></o:p>

3)对于类中的const常量,它只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。<o:p></o:p>

     不能在类声明中初始化const数据成员,只能在类构造函数的初始化表中进行,例如:<o:p></o:p>

class A<o:p></o:p>

{…<o:p></o:p>

const int SIZE=100;//错误,企图在类声明中初始化const数据成员<o:p></o:p>

int array[SIZE];//错误,位置的SIZE<o:p></o:p>

};<o:p></o:p>

应该是:<o:p></o:p>

class A <o:p></o:p>

{<o:p></o:p>

A(int size);<o:p></o:p>

const int SIZE;<o:p></o:p>

};<o:p></o:p>

A::A(int size):SIZE(size){<o:p></o:p>

…<o:p></o:p>

}<o:p></o:p>

若要建立在整个类中都恒定的常量,需要用枚举常量来实现,例如:<o:p></o:p>

class A{<o:p></o:p>

       enum{SIZE1=100,SIZE2=200};//<o:p></o:p>

       int array1[SIZE1];<o:p></o:p>

       int array2[SIZE2];<o:p></o:p>

};<o:p></o:p>

枚举常量不会占用对象的存储空间,它们在编译时被全部求值。枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点数.(PI=3.14159)<o:p></o:p>

<o:p> </o:p>

4、重载(overload)、覆盖(override)以及隐藏<o:p></o:p>

本来是讨论多态的,但是我们又讲到了重载这个概念,对于一个类中的成员函数,其被重载的特征:<o:p></o:p>

1)相同的范围(同一个类中);<o:p></o:p>

2)函数名相同,参数列表不同,返回值类型可相同也可不同。<o:p></o:p>

覆盖是指派生类函数覆盖基类函数,其特征:<o:p></o:p>

1)不同范围;<o:p></o:p>

2)函数名字相同,参数列表相同,<o:p></o:p>

3)基类必须要有virtual关键字。<o:p></o:p>

除覆盖外,所有同名的基类函数与子类函数都属于隐藏,下面是一个例子,讲得比较清楚,也点出了问题的本质:<o:p></o:p>

  1. class Base  
  2. {
  3. public:
  4.  Base();
  5.  virtual ~Base();
  6.  public:
  7.  virtual void f(float x)
  8.  {
  9.   cout << "Base f(float)" << x <<endl;
  10.  }
  11.  void g(float x)
  12.  {
  13.   cout<< "Base g(float)" << x <<endl;
  14.  }
  15.  void h(float x)
  16.  {
  17.   cout<< "Base h(float)" << x <<endl;
  18.  }
  19. };
  20. 派生类:
  21. class Derived:public Base
  22. {
  23. public
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics