高质量C++C编程指南笔记 标签: c++笔记 2015-11-22 20:59 179人阅读 评论(0) 收藏

时间:2022-12-20 07:33:40

1、  在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少 CPU 跨切循环层的次数。

2、  如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。

3、  即使程序真的不需要default 处理,也应该保留语句 default : break。

4、  C 语言用#define
来定义常量(称为宏常量)。 C++
语言除了 #define
外还可以用 const 来定义常量(称为 const
常量)。

5、  const 与 #define 的比较

Ø  const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。

Ø  有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

6、  需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。

7、  const 数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其 const 数据成员的值可以不同。

8、  不能在类声明中初始化const 数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道 SIZE 的值是什么。

class A

{…

const intSIZE = 100; // 错误,企图在类声明中初始化 const 数据成员

intarray[SIZE]; // 错误,未知的 SIZE

};

9、  const 数据成员的初始化只能在类构造函数的初始化表中进行。

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

11、             一般地,应将目的参数放在前面,源参数放在后面。

12、             有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。

13、             很多程序错误是由非法参数引起的,我们应该充分理解并正确使用“断言”( assert)来防止此类错误。

14、             return 语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。

15、             断言 assert 是仅在 Debug 版本起作用的宏,它用于检查“不应该”发生的情况。

16、             引用的一些规则如下:

Ø  引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。

Ø  不能有 NULL 引用,引用必须与合法的存储单元关联(指针则可以是 NULL)。

Ø  一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

17、             内存分配方式有三种:

Ø  从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量, static 变量。

Ø  在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

Ø  从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

18、             常见的内存错误及其对策如下:

Ø  内存分配未成功,却使用了它。

Ø  内存分配虽然成功,但是尚未初始化就引用它。

Ø  内存分配成功并且已经初始化,但操作越过了内存的边界。

Ø  忘记了释放内存,造成内存泄露。

Ø  释放了内存却继续使用它。(使用 free 或 delete 释放了内存后,没有将指针设置为 NULL。导致产生“野指针”。)

19、             数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。

20、             不能对数组名进行直接复制与比较。

21、             用运算符 sizeof 可以计算出数组的容量(字节数)。sizeof(p)得到的是一个指针变量的字节数,相当于 sizeof(char*),而不是 p 所指的内存容量。 C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。

22、             注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

23、             如果函数的参数是一个指针,不要指望用该指针去申请动态内存。(可以用指针的指针)

24、             发现指针 p 被 free 以后其地址仍然不变(非 NULL),只是该地址对应的内存是垃圾, p 成了“野指针”。如果此时不把 p 设置为 NULL,会让人误以为 p 是个合法的指针。

25、             free()之后由于指针所指向的内存已经被释放,所以其它代码有机会改写其中的内容,相当于该指针从此指向了自己无法控制的地方,也称为野指针。

26、             我们发现指针有一些“似是而非”的特征:

Ø  指针消亡了,并不表示它所指的内存会被自动释放。

Ø  内存被释放了,并不表示指针会消亡或者成了 NULL 指针。

27、             “野指针”的成因主要有两种:

Ø  指针变量没有被初始化。任何指针变量刚被创建时不会自动成为 NULL 指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为 NULL,要么让它指向合法的内存。

Ø  指针 p 被 free 或者 delete 之后,没有置为 NULL,让人误以为 p 是个合法的指针。

Ø  指针操作超越了变量的作用范围。

28、             malloc 与 free 是 C++/C 语言的标准库函数,new/delete 是 C++的运算符。它们都可用于申请动态内存和释放内存。

29、             对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free。因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符 new,以及一个能完成清理与释放内存工作的运算符 delete。注意 new/delete 不是库函数。

30、             由于内部数据类型的“对象”没有构造与析构的过程,对它们而言 malloc/free 和new/delete 是等价的。

31、             如果用 free 释放“ new 创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用 delete 释放“ malloc 申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。所以 new/delete 必须配对使用, malloc/free 也一样。

32、             malloc/free 的使用我们应当把注意力集中在两个要素上:“类型转换”和“ sizeof”。

Ø  malloc 返回值的类型是 void *,所以在调用 malloc 时要显式地进行类型转换,将 void * 转换成所需要的指针类型。

Ø  malloc 函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。

33、             如果 p 是 NULL 指针,那么 free 对 p 无论操作多少次都不会出问题。如果 p 不是 NULL 指针,那么 free 对 p连续操作两次就会导致程序运行错误。

34、             如果用 new
创建对象数组,那么只能使用对象的无参数构造函数。例如

Obj *objects = newObj[100]; // 创建100
个动态对象不能写成

Obj *objects = newObj[100](1);// 创建100

在用delete 释放对象数组时,留意不要丢了符号‘ []’。例如

delete []objects; // 正确的用法

delete objects; // 错误的用法

后者相当于delete objects[0],漏掉了另外 99
个对象。

35、             c++中为什么static成员函数不能声明为const?(注意是成员函数)

答:这是C++的规则,const修饰符用于表示函数不能修改成员变量的值,该函数必须是含有this指针的类成员函数,函数调用方式为thiscall,而类中的static函数本质上是全局函数,调用规约是__cdecl或__stdcall,不能用const来修饰它。

36、             重载和内联机制既可用于全局函数也可用于类的成员函数, const 与virtual 机制仅用于类的成员函数。

37、             只能靠参数而不能靠返回值类型的不同来区分重载函数。

38、             注意并不是两个函数的名字相同就能构成重载。全局函数和类的成员函数同名不算重载,因为函数的作用域不同。

39、             当心隐式类型转换导致重载函数产生二义性。

40、             成员函数被重载的特征:

Ø  相同的范围(在同一个类中);

Ø  函数名字相同;

Ø  参数不同;

Ø  virtual 关键字可有可无。

41、             覆盖是指派生类函数覆盖基类函数,特征是:

Ø  不同的范围(分别位于派生类与基类);

Ø  函数名字相同;

Ø  参数相同;

Ø  基类函数必须有virtual 关键字。

42、             “隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

Ø  如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。【参数不同一定会被隐藏】

Ø  如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。【没有virtual一定会被隐藏】

43、             参数缺省值只能出现在函数的声明中,而不能出现在定义体中。

44、             如果函数有多个参数,参数只能从后向前挨个儿缺省,否则将导致函数调用语句怪模怪样。

45、             在 C++运算符集合中,有一些运算符是不允许被重载的。这种限制是出于安全方面的考虑,可防止错误和混乱。

Ø  不能改变 C++内部数据类型(如int,float 等)的运算符。

Ø  不能重载‘ .’,因为‘ .’在类中对任何成员都有意义,已经成为标准用法。

Ø  不能重载目前 C++运算符集合中没有的符号,如#,@,$等。原因有两点,一是难以理解,二是难以确定优先级。

Ø  对已经存在的运算符进行重载时,不能改变优先级规则,否则将引起混乱。

46、             在 C 程序中,可以用宏代码提高执行效率。宏代码本身不是函数,但使用起来象函数。预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的 CALL调用、返回参数、执行 return 等过程,从而提高了速度。使用宏代码最大的缺点是容易出错,预处理器在复制宏代码时常常产生意想不到的边际效应。使用宏代码还有另一种缺点:无法操作类的私有数据成员。

47、             C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以*操作类的数据成员。所以在 C++ 程序中,应该用内联函数取代所有宏代码。

48、             关键字 inline
必须与函数定义体放在一起才能使函数成为内联,仅将 inline
放在函数声明前面不起任何作用。

49、             定义在类声明之中的成员函数将自动地成为内联函数。

50、             内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。

51、             以下情况不宜使用内联:

Ø  如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。

Ø  如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

52、             每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类 A,如果不想编写上述函数,C++编译器将自动为 A 产生四个缺省的函数,如:

Ø  A(void);                                          //缺省的无参数构造函数

Ø  A(const A &a);                             //缺省的拷贝构造函数

Ø  ~A(void);                                       //缺省的析构函数

Ø  A & operate =(const A &a);     //缺省的赋值函数

53、             构造函数与析构函数的另一个特别之处是没有返回值类型,这与返回值类型为 void 的函数不同。

54、             构造函数有个特殊的初始化方式叫“初始化表达式表”(简称初始化表)。初始化表位于函数参数表之后,却在函数体 {} 之前。这说明该表里的初始化工作发生在函数体内的任何代码被执行之前。

55、             类的 const 常量只能在初始化表里被初始化,因为它不能在函数体内用赋值的方式来初始化。

56、             类的数据成员的初始化可以采用初始化表或函数体内赋值两种方式,这两种方的效率不完全相同。

57、             非内部数据类型的成员对象应当采用第一种方式初始化,以获取更高的效率。

58、             构造和析构的次序:构造从类层次的最根处开始,在每一层中,首先调用基类的构造函数,然后调用成员对象的构造函数。析构则严格按照与构造相反的次序执行,该次序是唯一的,否则编译器将无法自动执行析构过程。

59、             成员对象初始化的次序完全不受它们在初始化表中次序的影响,只由成员对象在类中声明的次序决定。这是因为类的声明是唯一的,而类的构造函数可以有多个,因此会有多个不同次序的初始化表。如果成员对象按照初始化表的次序进行构造,这将导致析构函数无法得到唯一的逆序。

60、             拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。

61、             深度复制(针对《c++ primer plus》一书string例题的解释):复制构造函数应当复制字符串并将副本的地址赋给str成员,而不仅仅是复制字符串的地址,这样每个对象都有自己的字符串,而不是引用另一个对象的字符串,调用析构函数时都将释放不同的字符串,而不会试图去释放已经被释放的字符串。

62、             何时调用复制构造函数:新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用。每当程序生成了对象副本时,编译器都将使用复制构造函数。具体的说,当函数按值传递对象或函数返回对象时,都将使用复制构造函数。记住,按值传递意味着创建原始变量的一个副本。编译器生成临时对象时,也将使用复制构造函数。何时生成临时对象随编译器而异,但无论那种编译器,当按值传递和返回对象时,都将调用复制构造函数。

63、             String类的赋值函数要做的工作:

Ø  自我检查赋值的情况

Ø  释放成员指针以前指向的内存

Ø  复制数据而不仅仅是数据的地址

Ø  指向一个返回对象的引用(不要将 return *this 错写成 return this)

64、             基类与派生类的析构函数应该为虚(即加 virtual关键字)。

#include <iostream.h>

classBase

{

public:

virtual ~Base() { cout<< "~Base" << endl ; }

};

classDerived : public Base

{

public:

virtual ~Derived() { cout<< "~Derived" << endl; }

};

voidmain(void)

{

Base * pB = new Derived; // upcast

delete pB;

}

输出结果为:

~Derived

~Base

如果析构函数不为虚,那么输出结果为

~Base

65、             如果使用指向对象的引用或指针来调用虚方法,程序将使用为对象类型定义的方法,而不使用为引用或指针类型定义的方法。这称为动态联编或晚期联编。这种行为非常重要,因为这样基类指针或引用可以指向派生类对象。

66、             用 const
修饰函数的参数:
如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加 const
修饰,否则该参数将失去输出功能。const
只能修饰输入参数。(参数做输出用不能加const,const只能修饰输入参数)

67、             如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加 const
修饰。

68、             const 成员函数的声明看起来怪怪的: const 关键字只能放在函数声明的尾部,大概是因为其它地方都已经被占用了。

高质量C++C编程指南笔记 标签: c++笔记 2015-11-22 20:59 179人阅读 评论(0) 收藏的更多相关文章

  1. 苹果应用商店AppStore审核中文指南 分类: ios相关 app相关 2015-07-27 15&colon;33 84人阅读 评论&lpar;0&rpar; 收藏

    目录 1. 条款与条件 2. 功能 3. 元数据.评级与排名 4. 位置 5. 推送通知 6. 游戏中心 7. 广告 8. 商标与商业外观 9. 媒体内容 10. 用户界面 11. 购买与货币 12. ...

  2. 【搜索引擎Jediael开发笔记】v0&period;1完整代码 2014-05-26 15&colon;17 463人阅读 评论&lpar;0&rpar; 收藏

    详细代码请见 E:\Project\[重要]归档代码\SearchEngine归档代码 或 https://code.csdn.net/jediael_lu/jediael/tree/10991c83 ...

  3. 【搜索引擎Jediael开发笔记】V0&period;1完整代码 2014-05-26 15&colon;16 443人阅读 评论&lpar;0&rpar; 收藏

    详细代码请见 E:\Project\[重要]归档代码\SearchEngine归档代码 或 https://code.csdn.net/jediael_lu/jediael/tree/10991c83 ...

  4. 提高编程能力的7条建议 分类: T&lowbar;TALENT 2014-04-12 10&colon;41 294人阅读 评论&lpar;0&rpar; 收藏

    编程是非常酷的一件事情,但是在酷炫的背后它对很多人来说还是挺难的.很多人在学习编程之初就被困难击败了. 当你不熟悉编程的时候,你可能会觉得无从下手,并且不知道如何运用学到的知识.只要你通过了这一困难的 ...

  5. Shell脚本编程入门(一) 分类: 学习笔记 linux ubuntu 2015-07-09 21&colon;06 29人阅读 评论&lpar;0&rpar; 收藏

    最近在学shell,记录一下. if语句的使用: 1.判断两个参数大小 #!/bin/sh #a test about if statement a=10 b=20 if [ $a -eq $b ]; ...

  6. js笔记 标签: javascript 2016-08-01 13&colon;30 75人阅读 评论&lpar;0&rpar; 收藏

    typeof可以用来检测给定变量的数据类型,typeof是一个操作符而不是函数,所以圆括号可以省略. Undefined类型只有一个值,即特殊的undefined.在使用var声明变量但未对其加以初始 ...

  7. Latex笔记(参考文献) 分类: LaTex 2014-11-08 17&colon;41 239人阅读 评论&lpar;0&rpar; 收藏

    当你用LaTeX来写文档,在管理参考文献时,你可能会用到bibtex, 也许你会嫌麻烦,会选择用 \begin{thebibliography}{10} \bibitem xxxx \bibitem ...

  8. shell入门之函数应用 分类: 学习笔记 linux ubuntu 2015-07-10 21&colon;48 77人阅读 评论&lpar;0&rpar; 收藏

    最近在学习shell编程,文中若有错误的地方还望各位批评指正. 先来看一个简单的求和函数 #!/bin/bash #a test about function f_sum 7 8 function f ...

  9. Eclipse和MyEclipse的区别 分类: 编程工具 2015-07-18 11&colon;12 23人阅读 评论&lpar;0&rpar; 收藏

    今天,在一个Q群里有人问Eclipse和MyEclipse的区别.虽然对于知道的人来说答案很简单,但是对于不知道的人来说就很难,很多问题也都是这样的,会者不难,难者不会. 其实,网上搜搜答案就挺多的, ...

随机推荐

  1. &&num;39&semi;-&lbrack;&lowbar;&lowbar;NSCFString stringFromMD5&rsqb;&colon; unrecognized selector sent to instance 0x14d89a50&&num;39&semi;

    类型:ios 问题描述: 导入百度地图 然后在模拟器运行可以,真机测试不行: 报错: '-[__NSCFString stringFromMD5]: unrecognized selector sen ...

  2. &sol;proc&sol;net&sol;tcp中各项参数说明

    /proc/net/tcp中的内容由tcp4_seq_show()函数打印,该函数中有三种打印形式,我们这里这只列出状态是TCP_SEQ_STATE_LISTENING或TCP_SEQ_STATE_E ...

  3. react-native 金币彩带雨下落动画

    日常项目中,经常遇到一些表情雨/金币雨/彩带雨 等下落的动画,之前做android原生的时候,写过类似的效果,主要通过自定义view 在onDraw里绘制下落的过程,具体可以看下我的这篇github地 ...

  4. Java常用API——String字符串运算

    一.字符串运算 String类 1.概述 String是特殊的引用数据类型,它是final类. 2.构造方法 String str = "abc"; 相当于:  char date ...

  5. C语言输出格雷码

    格雷码是以n位的二进制来表示数. 与普通的二进制表示不同的是,它要求相邻两个数字只能有1个数位不同. 首尾两个数字也要求只有1位之差. 有很多算法来生成格雷码.以下是较常见的一种: 从编码全0开始生成 ...

  6. Spring Boot &plus; Spring Cloud 构建微服务系统(九):配置中心(Spring Cloud Config)

    技术背景 如今微服务架构盛行,在分布式系统中,项目日益庞大,子项目日益增多,每个项目都散落着各种配置文件,且随着服务的增加而不断增多.此时,往往某一个基础服务信息变更,都会导致一系列服务的更新和重启, ...

  7. python----运算符、布尔值

    一.运算符: + - * / ** // % 1,.   in ,not in 用法(判断某个东西是否在某个东西里面) name = '郑建文' 其中‘郑建文’是字符串, ‘郑’或‘建’或‘文’是一个 ...

  8. web api 权限控制

    https://www.cnblogs.com/landeanfen/p/5287064.html 我只是个搬运工, 我只想存个档

  9. &ast;&ast;&ast;解决UEditor编辑器无法插入第三方视频地址

    转:http://blog.csdn.net/qq_16241043/article/details/53894847 xssFilter导致插入视频异常,编辑器在切换源码的过程中过滤掉img的_ur ...

  10. CentOS下mysql安装

    一.检查环境 # 切换root 权限 su root # 检查是否安装过mysql rpm -qa|grep mysql # 删除所有mysql yum -y remove mysql* 1.上传文件 ...