以下的这些要点是对全部的C++程序猿都适用的。我之所以说它们是最重要的,是由于这些要点中提到的是你通常在C++书中或站点上无法找到的。如:指向成员的指针,这是很多资料中都不愿提到的地方,也是常常出错的地方,甚至是对一些高级的C++程序猿也是如此。

  这里的要点不不过解释如何写出更好的代码,很多其它的是展现出语言规则里面的东西。非常显然,它们对C++程序猿来说是永久的好资料。我相信这一篇文章会使你收获不小。



  首先,我把一些由不同层次的C++程序猿常常问的问题归到一起。我惊奇的发现有非常多是有经验的程序猿都还没意识到 .h 符号是否还应该出如今标准头文件里。





要点1: <iostream.h> 还是 <iostream>?




  非常多C++程序猿还在使用<iostream.h>而不是用更新的标准的<iostream>库。这两者都有什么不同呢?首先,5年前我们就開始反对把.h符号继续用在标准的头文件里。继续使用过时的规则可不是个好的方法。从功能性的角度来讲,<iostream>包括了一系列模板化的I/O类,相反地<iostream.h>只不过支持字符流。另外,输入输出流的C++标准规范接口在一些微妙的细节上都已改进,因此,<iostream>和<iostream.h>在接口和运行上都是不同的。最后,<iostream>的各组成都是以STL的形式声明的,然而<iostream.h>的各组成都是声明成全局型的。



  由于这些实质上的不同,你不能在一个程序中混淆使用这两个库。做为一种习惯,在新的代码中一般使用<iostream>,但假设你处理的是过去编写的代码,为了继承能够用继续用<iostream.h>旧保持代码的一致性。  





要点2:用引用传递參数时应注意的地方



  在用引用传递參数时,最好把引用声明为const类型。这样做的优点是:告诉程序不能改动这个參数。在以下的这个样例中函数f()就是传递的引用:
void f(const
int & i);

int main()

{

 f(2); /* OK */

}
  这个程序传递一个參数2给f()。在执行时,C++创建一个值为2的int类型的暂时变量,并传递它的引用给f().这个暂时变量和它的引用从f()被调用開始被创建并存在直到函数返回。返回时,就被立即删除。注意,假设我们不在引用前加上const限定词,则函数f()可能会更改它參数的值,更可能会使程序产生意想不到的行为。所以,别忘了const。



  这个要点也适用于用户定义的对象。你能够给暂时对象也加上引用假设是const类型:
struct A{};

void f(const A& a);

int main()

{

 f(A()); // OK,传递的是一个暂时A的const引用

}


要点3:“逗号分离”表达形式




 “逗号分离”表达形式是从C继承来的,使用在for-和while-循环中。当然,这条语法规则被觉得是不直观的。首先,我们来看看什么是“逗号分离”表达形式。



  一个表达式由一个或多个其他表达式构成,由逗号分开,如:
 if(++x, --y, cin.good())
//三个表达式
  这个if条件包括了三个由逗号分离的表达式。C++会计算每一个表达式,但完整的“逗号分离”表达式的结果是最右边表达式的值。因此,仅当cin.good()返回true时,if条件的值才是true。以下是还有一个样例:
int j=10;


int i=0;

while( ++i, --j)

{

 //直到j=0时,循环结束,在循环时,i不断自加

}


要点4,使用全局对象的构造函数在程序启动前调用函数




  有一些应用程序须要在主程序启动前调用其他函数。如:转态过程函数、登记功能函数都是必须在实际程序执行前被调用的。最简单的办法是通过一个全局对象的构造函数来调用这些函数。由于全局对象都是在主程序開始前被构造,这些函数都将会在main()之前返回结果。如:

class Logger

{

 public:

 Logger()

  {

   activate_log();//译者注:在构造函数中调用你须要先执行的函数

  }

};

Logger log; //一个全局实例

int main()

{

 record * prec=read_log();//译者注:读取log文件数据

 //.. 程序代码

}

  全局对象log在main()执行之前被构造,log调用了函数activate_log()。从而,当main()開始执行时,它就能够从log文件里读取数据。

  毫无疑问地,在C++编程中内存管理是最复杂和最easy出现bug的地方。直接訪问原始内存、动态分配存储和最大限度的发挥C++指令效率,都使你必须尽力避免有关内存的bug。

  

要点5:避免使用复杂构造的指向函数的指针



  指向函数的指针是C++中可读性最差的语法之中的一个。你能告诉我以下语句的意思吗?

void (*p[10]) (void (*)());
  P是一个“由10个指针构成的指向一个返回void类型且指向还有一个无返回和无运算的函数的数组”。这个麻烦的语法真是让人难以辨认,不是吗?你事实上能够简单的通过typedef来声明相当于上面语句的函数。首先,使用typedef声明“指向一个无返回和无运算的函数的指针”:
typedef void (*pfv)();
  接着,声明“还有一个指向无返回且使用pfv的函数指针”:
typedef void
(*pf_taking_pfv) (pfv);
  如今,声明一个由10个上面这种指针构成的数组:
pf_taking_pfv p[10];
  与void (*p[10]) (void (*)())达到相同效果。但这样是不是更具有可读性了!



要点6:指向成员的指针




  一个类有两种主要的成员:函数成员和数据成员。相同的,指向成员的指针也有两种:指向函数成员的指针和指向数据成员的指针。后则事实上并不经常使用,由于类通常是不含有公共数据成员的,仅当用在继承用C写的代码时协调结构(struct)和类(class)时才会用到。



  指向成员的指针是C++语法中最难以理解的构造之中的一个,可是这也是一个C++最强大的特性。它能够让你调用一个类的函数成员而不必知道这个函数的名字。这一个很敏捷的调用工具。相同的,你也能够通过使用指向数据成员的指针来检查并改变这个数据而不必知道它的成员名字。

  指向数据成员的指针



  虽然刚開始时,指向成员的指针的语法会使你有一点点的迷惑,但你不久会发现它事实上同普通的指针差点儿相同,仅仅只是是*号的前面多了::符号和类的名字,例:定义一个指向int型的指针:

int * pi;
  定义一个指向为int型的类的数据成员:
int A::*pmi; //pmi是指向类A的一个int型的成员
  你能够这样初始化它:
class A

{

 public:

 int num;

 int x;

};

int A::*pmi = & A::num;
  上面的代码是声明一个指向类A的一个int型的num成员并将它初始化为这个num成员的地址.通过在pmi前面加上*你就能够使用和更改类A的num成员的值:
A a1, a2;

int n=a1.*pmi; //把a1.num赋值给n

a1.*pmi=5; // 把5赋值给a1.num

a2.*pmi=6; // 把6赋值给6a2.num
  假设你定义了一个指向类A的指针,那么上面的操作你必须用
->*操作符取代:
A * pa=new A;

int n=pa->*pmi;

pa->*pmi=5;


  指向函数成员的指针



  它由函数成员所返回的数据类型构成,类名后跟上::符号、指针名和函数的參数列表。举个样例:一个指向类A的函数成员(该函数返回int类型)的指针:
class A


{

 public:

 int func ();

};

int (A::*pmf) ();

  上面的定义也就是说pmf是一个指向类A的函数成员func()的指针.实际上,这个指针和一个普通的指向函数的指针没什么不同,仅仅是它包括了类的名字和::符号。你能够在在不论什么使用*pmf的地方调用这个函数
func():

pmf=&A::func;

A a;

(a.*pmf)(); //调用a.func()
  假设你先定义了一个指向对象的指针,那么上面的操作要用->*取代:
A *pa=&a;

(pa->*pmf)(); //调用pa->func()

  指向函数成员的指针要考虑多态性。所以,当你通过指针调用一个虚函数成员时,这个调用将会被动态回收。还有一个须要注意的地方,你不能取一个类的构造函数和析构函数的地址。

要点7、避免产生内存碎片



  常常会有这种情况:你的应用程序每执行一次时就由于程序自身缺陷而产生内存漏洞而泄漏内存,而你又在周期性地反复着你的程序,结果可想而知,它也会使系统崩溃。但如何做才干预防呢?首先,尽量少使用动态内存。在大多数情况下,你可能使用静态或自己主动存储或者是STL容器。第二,尽量分配大块的内存而不是一次仅仅分配少量内存。举个样例:一次分配一个数组实例所需的内存,而不是一次仅仅分配一个数组元素的内存。

要点8、是delete还是delete[]



  在程序猿中有个荒诞的说法:使用delete来取代delete[]删除数组类型时是能够的!

  举个样例吧:

 int *p=new
int[10];

 delete p; //错误,应该是:delete[] p

  上面的程序是全然错误的。其实,在一个平台上使用delete取代delete[]的应用程序或许不会造成系统崩溃,但那纯粹是运气。你不能保证你的应用程序是不是会在还有一个编译器上编译,在还有一个平台上执行,所以还是请使用delete[]。

要点9、优化成员的排列



  一个类的大小能够被以下的方式改变:

struct A

{

 bool a;

 int b;

 bool c;

}; //sizeof (A) == 12
  在我的电脑上。这个结果可能会让你惊讶,由于A的成员总数是6个字节:1+4+1个字节。那另6字节是哪儿来的?编译器在每一个bool成员后面都插入了3个填充字节以保证每一个成员都是按4字节排列,以便分界。你能够降低A的大小,通过下面方式:
struct B

{

 bool a;

 bool c;

 int b;

}; // sizeof (B) == 8
  这一次,编译器仅仅在成员c后插入了2个字节。由于b占了4个字节,所以就非常自然地把它当作一个字的形式排列,而a和c的大小1+1=2,再加上2个字节就刚好按两个字的形式排列B。


要点10、为什么继承一个没有虚析构函数的类是危急的?




  一个没有虚析构函数的类意味着不能做为一个基类。如std::string, std::complex, 和 std::vector 都是这种。为什么继承一个没有虚析构函数的类是危急的?当你公有继承创建一个从基类继承的相关类时,指向新类对象中的指针和引用实际上都指向了起源的对象。由于析构函数不是虚函数,所以当你delete一个这种类时,C++就不会调用析构函数链。举个样例说明:
class A

{

 public:

 ~A() // 不是虚函数

 {

 // ...

 }

};

class B: public A
//错; A没有虚析构函数

{

 public:

 ~B()

 {

 // ...

 }

};

int main()

{

 A * p = new B; //看上去是对的

 delete p; //错,B的析构函没有被调用

}



要点11、以友元类声明嵌套的类




  当你以友元类声明一个嵌套的类时,把友元声明放在嵌套类声明的后面,而不前面。
class A


{

 private:

 int i;

 public:

 class B //嵌套类声明在前

 {

  public:

  B(A & a) { a.i=0;};

 };

 friend class B;//友元类声明

};
  假设你把友元类声明放在声明嵌套类的前面,编译器将抛弃友元类后的其他声明。

C++要点的更多相关文章

  1. C++常见笔试面试要点以及常见问题

    1. C++常见笔试面试要点: C++语言相关: (1) 虚函数(多态)的内部实现 (2) 智能指针用过哪些?shared_ptr和unique_ptr用的时候需要注意什么?shared_ptr的实现 ...

  2. 《高性能javascript》一书要点和延伸(上)

    前些天收到了HTML5中国送来的<高性能javascript>一书,便打算将其做为假期消遣,顺便也写篇文章记录下书中一些要点. 个人觉得本书很值得中低级别的前端朋友阅读,会有很多意想不到的 ...

  3. [php]laravel框架容器管理的一些要点

    本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...

  4. 基础笔记(一):C#编程要点

    前言 来源于手中日常摘录的资料和书籍,算是对看过的东西的总结,部分注有阅读心得,也有部分只提出大纲或结论.(备注:本篇文章中大部分要点需要有实际的开发经验,有助于阅读理解.)     目录 const ...

  5. CORS基础要点:关于dataType、contentType、withCredentials

    事实上,面试时我喜欢问跨域,因为多数开发者都知道它并且常用,而我希望能从面试者的回答中知道他在这个问题的深入程度,进一步看看面试者研究问题的思维方式及钻研精神,然而确实难到了很多人,当然这也不是面试通 ...

  6. 漫谈C++:良好的编程习惯与编程要点

    以良好的方式编写C++ class 假设现在我们要实现一个复数类complex,在类的实现过程中探索良好的编程习惯. ① Header(头文件)中的防卫式声明 complex.h: # ifndef ...

  7. 推荐Linux管理员不可不知十大PHP安全要点 - SCutePHP

    PHP是使用最广泛的脚本编程语言之一.市场份额颇能说明其主导地位.PHP 7已推出,这个事实让这种编程语言对当前的开发人员来说更具吸引力.尽管出现了一些变化,但是许多开发人员对PHP的未来持怀疑态度. ...

  8. PHPCMS与UCenter整合要点

    要点一: PHPCMS不能直接与UCenter整合,而是要经过 PHPSSO 适配,因此应用主URL应是 http://phpcms_url/phpsso_server 这种模式的. 要点二: 因为 ...

  9. EDM制作要点

    EDM是Email Direct Marketing的缩写,虽然也是html,但是和我们在网页上使用的html不同,因为安全原因,各大邮箱服务商级邮件客户端都会对邮件内容进行一定程度上的处理,不会按照 ...

  10. 第一次react-native项目实践要点总结

    今天完成了我的第一个react-native项目的封包,当然其间各种环境各种坑,同时,成就感也是满满的.这里总结一下使用react-native的一些入门级重要点(不涉及环境).注意:阅读需要语法基础 ...

随机推荐

  1. Uva 552 Prime Ring Problem(dfs)

    题目链接:Uva 552 思路分析:时间限制为3s,数据较小,使用深度搜索查找所有的解. 代码如下: #include <iostream> #include <string.h&g ...

  2. JAX-RS

    一.简介 JAX-RS(Java API for RESTful Web Services),是JAVAEE6中提出的Java 编程语言的应用程序接口,支持按照表述性状态转移(REST)架构风格创建W ...

  3. Embedded Linux Primer----嵌入式Linux基础教程--2.4节--嵌入式Linux发行版

    嵌入式Linux发行版 究竟什么是Linux发行版?在Linux内核引导之后,它期望找到并挂载根文件系统.当一个匹配的根文件系统已经挂载上,启动脚本开始运行大量程序和系统要求的工具.这些程序经常调用其 ...

  4. struts2的总体回想(ACTION、拦截器、值栈、OGNL表达式、ModelDriven方案等)

    ValueStack:struts2的一个存放数据的数据结构(核心) ValueStack大致能够理解为:由Map和对象栈组成 ValueStack作用范围:一个请求,用它来取代request的作用域 ...

  5. 史上最全然oophper php文件上传之文件类型相应表,ie,火狐各一份。

    ie 火狐 id 后缀名 php识别出的文件类型 0 gif image/gif 1 jpg image/jpeg 2 png image/png 3 bmp image/bmp 4 psd appl ...

  6. ceph存储之ceph客户端

    CEPH客户端: 大多数Ceph用户不会直接往Ceph存储集群里存储对象,他们通常会选择Ceph块设备.Ceph文件系统.Ceph对象存储之中的一个或多个: 块设备: 要实践本手册,你必须先完成存储集 ...

  7. HashMap,LinkedHashMap,TreeMap的区别(转)

    Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复.Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快 ...

  8. Ubuntu14.04 Y460闪屏问题解决方案

    我的笔记本是联想Y460,安装了Ubuntu之后发现屏幕闪烁移位,而且在使用IDE的时候出现无法输入中文等问题,其实是显卡驱动的问题,N卡官网给的驱动不好用,尝试使用大黄蜂 参考:https://wi ...

  9. CodeForces 22B Bargaining Table 简单DP

    题目很好理解,问你的是在所给的图中周长最长的矩形是多长嗯用坐标(x1, y1, x2, y2)表示一个矩形,暴力图中所有矩形易得递推式:(x1, y1, x2, y2)为矩形的充要条件为: (x1, ...

  10. 同步fifo的verilogHDL设计实例

    原创 设计一个fifo,输入16bit,输出16bit的data,寻址宽度5bit,有空满标志. top 层如下所示: /* date : 2014/10/14 version : modelsim ...