C++编程思想前16章重难点记录和理解

C++编程思想前16章重难点记录和理解


下面附上我的笔记

一、对象导言 二、对象的创建与使用
1.静态类型检查:在第一次编译中,检查函数参数是否正确。动态类型检查:程序运行时作部分类型检查
2.extern声明变量而不定义(定义分配内存,声明不用)
3.#include <iostream.h>相当于#include 和using namespace std
4.getline()遇到换行符终止,丢弃换行符;f()在C中表示不确定参数,C++中表示无参数
三、C++中的C
6.static/extern:全局变量:默认为存储在静态区域,static表示它是内部连接的,extern表示它是外部连接的(默认);局部变量:static表示在静态存储区,extern表示某处已经存在存储区
7.内部连接:只对正被编译的文件创建存储空间,static指定;外部连接:为所有被编译过的文件创建一片单独的存储区域,extern指定
8. 命名别名:typedef unsigned long ulong;
9. union联合:把所有数据放在一个单独空间,大小为最大项的字节数,因为其中不同类型的数据在同一内存区覆盖存放;union不能在继承是作为基类;匿名联合:没有名字,访问成员不用•,直接访问P178。
10. 数组不可按值传递:不会自动得到传递给函数的数组的本地拷贝,修改数组时一直在修改外部对象
11. 函数指针void (fptr)()=func;(没有括号和参数,可以用&也可以不用)
12. C和C++中都可以给void
赋值任何指针,C中也可给任何指针赋值void*,C++不可以
四、数据抽象
13. 相比用过程实现CStash,使用类实现的好处:名字冲突和信息隐藏。C到C++的根本改变是把函数放到struct中(chapter4),称为封装。
14. 头文件里的内容:只限于声明,只限于对于编译器的信息,不涉及通过生成代码或创建变量而分配存储的任何信息
15. 一次定义规则:可以对事物声明多次,只能定义一次;可重复声明函数,不能重复声明结构P134(用#ifdef解决)
16. 不要在头文件中使用using namespace XXX ; 纯作用域解析符::表示全局变量
五、隐藏实现
17. 友元的声明也是函数的声明,无需再次声明;而编译器在被告知类是友元之前,必须先声明类的存在
18. 嵌套结构不能访问private成员,但是可以声明为嵌套友元P146
19. 数组大小:sizeof c/sizeof c(数组大小除以第一个元素大小)
六、初始化与清除
20. 默认构造函数就是不带任何参数的构造函数(用户写的不带参数的也算),创建一个对象而不知任何细节时使用,当且仅当一个struct或class无构造函数时会自动创建
七、函数重载与默认参数
21. 函数重载:改变参数,不能通过改变返回值
22. 占位符参数,void f(int x,int,float flt){}没有名字的参数,不能使用,以后可以修改函数定义而不需修改所有函数调用
23. 默认参数在函数声明时给定参数值,定义时不用
24. 重载和默认参数选择:不能把默认参数作为标志去确定执行函数的哪部分,如果可以,尽量把函数分解成重载函数;
八、常量
25. 必须把const放在头文件里,C++默认为内部连接,(C默认外部连接),const默认不分配空间,但是当用extern声明或者const变量在类里或者求其地址或者编译时不知何值(如:需要用户输入)就需要为其分配存储空间
26. 编译器编译期间不访问内存所以const int i[]={1,2,3,4};//!float f[i[3]];非法
27. char cp=”happy”;写法被认为是常量,不能修改其中的任何字符;若想修改,写成char cp[]=”happy”;
28. 把一个临时对象传递给一个接受const引用的函数是可能的,但不能把它传递给接受指针的函数P195
29. const成员变量:在建立时不给它初值,因为每个对象需要不同的值;也不能在构造函数里赋值,因为const不能赋值只能初始化;所以需要构造函数初始化列表P197
30. 构造函数初始化列表:在进入构造函数之前完成的初始化;const成员,引用成员,类成员没有默认构造函数的类类型,基类构造函数这四种必须通过初始化列表初始化,其他随意
31. static const:类中的一个成员常量(内建类型),在该类的所有对象中都一样;必须在定义的地方初始化P198;
但是用户自定义的类型的static const成员对象必须在外部进行定义P240
32. const成员函数:const对象只能调用const成员函数,因为该函数不会改变任何成员变量P200
33. 改变const对象方法:强制转换((Y
)this)->i++;mutable关键字:指定一个数据成员可以在const对象中被改变
九、内联函数
34. 内联:任何在类中定义的函数均为内联函数,非类的函数前加inline关键字表示内联,函数体必须和声明在一起;内联函数可以减少函数调用的开销,但是会使代码膨胀。一般把内联定义放在头文件里。
35. 不适用内联函数:函数太复杂;要显示或隐式地取函数地址。(即使声明内联,明智的编译器也会适当地选择P219)
36. 类中的内联函数:引用一个未声明的函数是可以的,因为C++中只有在类声明结束后才会计算内联函数
37. #s字符串输出;a##_string标志粘贴P222
十、名字控制
38. 名字空间P233:(1)定义namespace MyLib{}(无分号)(2)别名namespace Bob=BobSuperDuper;(3)不能创建名字空间的实例(4)每个翻译单元只能有一个匿名名字空间,在该单元无限制有效(5)把一个局部名字放在匿名名字空间中,不用static就是内部连接的;(6)使用:作用域解析:A::B::C、使用指令:using namespace std(不放在头文件里)、使用声明:using V::f()(使用声明比指令优先级高)using声明可以引起一个函数用相同的参数类型来重载
39. 局部类(函数中的类)不能有静态数据成员P241
40. 静态成员函数:没有this指针,为类的全体对象服务,可以不用对象.函数调用,直接V::f调用即可;只能访问静态数据成员,只能调用静态成员函数
41. 令类只有一个实例:私有构造函数P243
42. 替代连接说明:C++中用C库:extern “C” float f()
十一、引用和拷贝构造函数
43. 引用必须和存储单元相联系,访问引用就是访问存储单元;创建时必须初始化,一旦指向某对象,不能变更指向;参数传递通常使用传引用,但是当传值是唯一安全的途径时,就用传值
44. 拷贝构造函数:X(X&)自动调用的场景:一个对象以传值方式传入函数;一个对象以传值方式从函数返回;一个对象需要通过另一个对象进行初始化。A a();A f(A x){return x;} A b=f(a);过程:x是a的拷贝,函数中使用的都是a的拷贝,返回时x成为a的拷贝的拷贝,出右括号时,a的拷贝被析构,a的拷贝的拷贝赋值给b。
45. 如果我们不显式声明拷贝构造函数,则编译器会创建默认的拷贝构造函数(位拷贝),容易出错,需制止。替代拷贝构造函数:禁止按值传递:声明私有拷贝构造函数,可以不定义;使用改变外部对象的函数时,用传引用P267
46. 指向成员的指针:对象指针->成员指针;对象/引用.成员指针;
十二、运算符重载
47. 在仅包含内置数据类型的表达式中运算符是不能被重载的,只有包含用户定义类型的表达式才能有重载运算符
48. 某个参数如果在子程序或函数中没有用到,那就被称为哑元。
49. 运算符重载中所有赋值运算都需要检测自赋值,即是否自己给自己赋值
50. 临时对象语法:Integer(left.i+right.i)创建一个临时Integer对象并返回它,它没有名字,仅需要一个普通构造函数调用,且不调用析构函数,不调用拷贝构造函数(效率高 )
51. 一般的运算符重载最好都使用成员运算符,<<和>>用非成员运算符
52. 在任何时候使用一个“=”代替构造函数调用来初始化一个对象时,无论等号右侧是什么,编译器都会试图寻找一个接受右边类型的构造函数P298
53. 重载运算符时,编译器强制operator=为成员运算符;对象的赋值很常用,所以如果用户没有重载针对特殊对象的赋值符,编译器将会自动创建一个(将可能递归调用成员对象的赋值符)
54. 自动类型转换:一个类型的构造函数以另一个类型的对象为唯一参数时,可以将另一类型对象悄悄转换为当前类型对象P306,为了防止悄悄进行,可在构造函数前加关键字explicit,就需要显示调用该构造函数才能转换;运算符重载法:创建一个成员函数,operator后加目标类型名称,无返回类型,返回类型就是该类型P307。
二者比较:使用构造函数法无法实现从用户定义类型向内置类型的转换;全局版本的运算符重载可以针对左右任意一操作数,而成员版本则必须保证左侧操作数正确
十三、动态对象创建
55. 创建一个对象会:为对象分配内存调用构造函数初始化内存
56. 三种储存区域:静态区域(static)、栈(常规)、堆(new)
57. new表达式:在堆中为对象分配内存,并为其调用构造函数,当new分配内存失败时,返回0,此时不会调用构造函数0,构造函数无参数时,可以没有括号。。。=new MyType;
delete表达式:调用析构函数,再释放内存;delete只用于new对象;delete一个void指针,指挥释放内存,因为没有类型信息,不知道调用哪个析构函数;但是如果只是一个无析构函数的简单对象,就无所谓
58. 删除数组:delete[]fp;将从数组创建时放在某处的对象数量取回,并为其中所有对象调用析构函数
59. 伪构造函数new int(5)产生一个int指针,不是真正的构造函数,只是赋值
60. 重载new和delete只能改变内存的分配和释放P327
全局重载:void
operator new(size_t sz){/仅仅完成了内存分配/} void operator delete(void
m){}
十四、继承和组合
61. 继承class Y:public X{}Y继承X,public表示基类X在Y中原来公有的还是公有,私有的还是私有,若无public或者显式声明private,基类成员全部为私有,并且一个对象不能看做基类的实例,想要基类成员任何一个可视,只要在派生类中public声明即可,但是给出public一个重载函数的名字将会使基类中所有重载版本公有化
62. 构造函数调用顺序:先调用基类构造函数、再根据声明顺序调用成员对象构造函数、最后是本类的构造函数;拷贝构造函数一样的顺序,operator=也是一样的顺序
63. 派生类修改基类函数:改变返回类型、改变参数、改变内容,都会使基类所有同名函数被隐藏;对于虚函数,重新定义仍会隐藏,但是不允许改变重定义函数的返回值
64. 不能被自动继承的函数:构造、析构、operator=,在继承时,如果不亲自创建它们,编译器就会生成它们。生成的构造函数使用成员方式的初始化,被生成的operator=使用成员方式的赋值
65. 自动类型转换只发生在函数调用时作为参数,不能依靠它来调用目标类的成员,f(One)√ one.close()×P351
十五、多态性和虚函数
66. 晚捆绑(运行时捆绑)只对virtual函数有用,virtual只需在声明中使用,在定义时不用。基类中一个函数被声明为virtual,那么在所有派生类中都是虚的。虚函数:可以根据调用成员函数的对象来判断调用基类版本还是派生类版本,当确定的类型无该函数的重写版本,调用继承层次中最近的该函数
67. 晚捆绑实现机制:编译器对每个包含虚函数的类创建一个VTABLE,放置其中虚函数的地址(所有的VTABLE有相同的顺序),在每个包含虚函数的类中,有一个VPTR,指向VTABLE。当通过基类指针做虚函数调用时,编译器能通过VPTR获得对应函数地址。一个class 的大小为其中成员对象的大小之和加上一个void
指针[即VPTR]的大小,若类型中没有数据成员,编译器会强制该类型长度非0(可能为1),因为每个对象必须有一个互相区别的地址
68. 虚函数不能为内联函数,因为需要取地址,而内联函数是在符号表里,即使写成内联,编译器也不会把它当成内联的;但是纯虚函数禁止写成内联形式P378
69. 向上类型转换仅处理地址,例:(对象向上类型转换会发生切片,用纯虚函数禁止)
(1)Dog ralph; Pet *p1=&ralph; p1->speak() (2) Pet *p2=new Dog(“bob”) p2->speak()这里p1和p2都是Pet指针,不确定实际类型,所以要使用虚函数机制,调用的都是Dog的speak();
(3)//!Pet p3=new Dog(“pip”); p1->sit();sit()是派生类新添加的虚函数,p3是Pet指针,所以无法调用它
若用(Dog
)p3->sit();调用即可 P379切片 (向下类型转换不安全,所以必须知道原确切类型才能转)
(4)Dog ralph;Pet pet=ralph;pet.speak();调用Pet版本
70. 纯虚函数 virtual void f()=0声明,可以不要定义;有此声明的类成为抽象类,继承一个抽象类必须实现所有纯虚函数,否则派生类也是抽象类;抽象类不能被实例化,因为有纯虚声明的抽象类的VTABLE不完全(因为没有放置纯虚函数地址);类中全是纯虚函数的称为纯抽象类;纯虚函数禁止对抽象类的函数以传值方式调用(因为不能实例化),所以可以保证向上类型转换时总是使用指针或引用
73.虚函数不能是static的,静态成员函数,可以不通过对象来调用,没有隐藏的this指针。
virtual函数一定要通过对象来调用,有隐藏的this指针。
74.对一个对象的值进行向上类型转换,对象属性被切片,转换后调用的函数均为基类版本;对一个对象的引用(地址)进行向上类型转换,该对象的引用的虚函数被切片,转换后调用的函数为派生类版本,但是调用派生类新添的虚函数会报错
71. 对于在构造函数和析构函数中调用虚函数的情况,被调用的只是该函数的本地版本 P386
72. 虚析构函数是为了正确调用一个指向派生类的基类型指针的析构函数,所以析构函数常常得是虚的;调用机制:先调用派生类的析构函数,再调用基类的析构函数,与构造函数顺序相反
73. 纯虚析构函数必须有函数体,但是不能内联写
为什么构造函数不能为虚函数?
1、从存储空间角度:虚函数的vtable是存储在对象的内存空间的。而对象还没有实例化,内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。2、从使用角度:虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,使用虚函数也没有实际意义。3、而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。4、创建一个对象时要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它。5、从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数
C++模板

  1. 非内联函数的定义需要和其声明写在一个文件中,或者在.h文件中#include .cpp文件。2.template表示其后的东西编译器不会分配存储空间,一直等待直到被告知类型。3.懒惰初始化:不在构造函数中初始化,在第一次访问时初始化(如果创造大量对象,但不访问每一个时,可以使用以节省内存)。4.迭代器 是一个对象。

Java泛型
泛型方法:如果调用f()时传入了基本数据类型,自动打包机制将会被触发,将基本数据类型包装为对应的对象。有自动类型转换。List ls = new ArrayList();List lo = ls;(不可以)
通配符泛型类型: extends换成super表示一个类的父类,?表示类型实参。类型推断表示Java编译器查看方法调用及其对应的声明,以检查和确定类型参数。 推断算法检查参数的类型,如果可用,则返回分配的类型。 推断算法尝试找到一个可以填满所有类型参数的特定类型。泛型是使用一种称为类型擦除的方法实现的。编译器使用泛型类型信息来编译代码,但之后将其删除。泛型出现在编译时。一旦编译器确认泛型类型被安全地使用,它就会将其转换为原始类型。泛型类型在逻辑上可以看成多个不同类型,实际上都是基本类型。限制:不能用泛型new E(),new E[],运行时泛型无效;

Java collection:支持两类容器:1.元素集合(collection) 2.键值对(map)

set:储存不重复元素的容器。HashSet:添加到散列集的对象需要以适当分散散列码的方式实现散列码方法。LinkedHashSet:它扩展了HashSetwith一个链表实现,支持对集合中的元素排序。SortedSet:SortedSet是Set的子接口,它保证集合中的元素被排序。TreeSet:TreeSet实现了排序集接口。您可以将对象添加到树集合中,只要它们可以相互比较。

list:存储可重复的元素。ArrayList :ArrayList将元素存储在数组中。如果需要,将动态创建并增加数组。LinkedList:将元素储存在链表中。How to choose:如果应用程序不需要插入或删除元素,那么数组是最有效的数据结构。
Map:存储键值对。
Multiset:一组计数器Multiset mset = HashMultiset.create();mset.count(“to”);
Multi-map: 一个key对应一个value集合。Multimap<String, String> mmap = TreeMultimap.create();mmap.put(“D”, “Gore”);
mmap.put(“D”, “Clinton”);
BiMap: 双向映射。a到b和b到a对称;避免尝试“反转”映射或存储逆映射。BiMap<String, String> bmap = HashBiMap.create();mmap.put(“Arizona”, “Phoenix”);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值