C++语言中级

《c++语言中级》 知识点总结:
1. 类型决定了:大小、行为、存储方式、编译器检查依据;
每个变量都有name、type、address、value、reference;
变量生存周期从短到长: 临时、栈区、全局静态、堆区;
2. 对象在内存中的布局:
对象------仅有数据成员(非静态)this;
函数------代码区
3. 重载:同一个作用域内的几个函数,函数名相同,形参表一定不同(个数、类型、次序)。
4. 成员函数的默认形参(从后往前)与函数的重载不允许同时出现--------避免二义性。
5. 常函数的const修饰的是this指针。
6. 类中的成员函数如果不调用,可以不实现。
7. Inline函数在编译时替换,要尽量简洁,不允许有循环、switch、break、递归、goto、异常等。
8. C语言没有引用,&为取地址符。C++才有引用类型,int &b =a; 引用变量可以传递定义int I = 5, int &ii = I; int &iii = ii;可以定义对指针的引用,int *p = &I, int &r = *p;
9. 构造函数: 申请空间,执行初始化列表,执行构造函数体。
初始化列表按照成员数据声明的次序执行。
构造函数调用形式:
显示调用---------clock *p = new clock(8,30);
隐式调用---------clock myclock(8,30);
Explicit关键字关闭构造函数的隐式类型转换功能【float f(100);  A a=10;  A a(10);都暗含隐式类型转换】。
默认构造函数: 没有参数,仅申请内存空间,构造对象。
10. 拷贝构造函数调用场景:
(1). 直接调用  Clock a;  clock b(a);
(2). 形实结合  void fun(clock x);  fun(a);  
(3). 返回对象  clock fun(void);
(4). Clock b =a;//拷贝构造           clock s;   s=a;//赋值而非拷贝构造
默认拷贝构造函数:申请空间,创建对象,并不初始化。
11. 析构函数调用场景:
(1). }块结束或文件结束;    (2). Goto;    (3).异常catch();     (4).delete([])
12. C++能自动产生的6个函数: 构造、拷贝构造、赋值、析构、取址、取常址。
13. 引用相当于指针,在类中占存储空间(但引用在类外不占用空间???)
14. 不要让成员函数返回一个非常引用或常指针(如何理解???)。
15. 类之间的关系有:组合、继承、友元、聚集、内嵌、局部、模板;
C++中struct、union也是类,class默认为private,struct默认为public;
类中镶嵌enum提供类内使用的常量.
16. 初始化列表:
(1). 初始化列表应罗列全部组合对象;
(2). 被组合类一定要提供无参构造函数;
(3).初始化列表必须按照数据成员的声明次序写,构造函数和拷贝构造函数可以使用初始化列表;
(4).常用于类组合和类继承,适用于对象初始化,在创建对象时完成的工作皆应交由初始化列表去做,在构造函数体内赋值已经晚了,因为对象创建已经结束了;
(5).类成员中的const对象和引用必须用初始化列表,因为这两种对象声明后必须马上初始化,在构造函数中赋值是不行的。
17. 必须用初始化列表的场合:
(1).引用和指针成员;
(2).常数据成员;
(3).被组合的类的构造函数带参,而组合类创建对象时;
(4).基类构造函数带参,子类创建对象时;
(5).派生类的构造函数应该在初始化列表中调用基类的构造函数;
18. 名字的搜索次序: 本模块--->本类--->本命名空间--->全局
19. 类的静态成员:
静态成员函数没有this指针,因而不能直接访问对象的数据成员;
类的静态数据成员和函数成员具有类属性,不占用对象内存空间;
静态不继承,静态成员函数不能定义为虚函数,不必重载也不必为常函数。
20. 友元
分为友元函数,友元成员函数,友元类;
友元函数不能和寄生类的成员函数同名;
友元和静态都通过”对象名.”访问,一般以参数形式传入;
21. 只有类的成员函数才能为常函数,常函数不能修改类的数据成员、常对象、常数据成员、常引用;
常对象不能修改数据成员,不能调用非长成员函数。
22. friend仅于声明;
inline仅放于实现;
static放于类内声明,类外不带;
默认形参在声明时带,实现时不带;
常函数Const在声明和实现都带,常函数const修饰的是this,因而不能为静态;
void print() 和void print() const 两者的实际参数表不同,为重载。
23. 建议: 不修改数据成员的函数都应定义为常函数,常对象只能调用常函数,非常对象既可以调用常函数,也可以调用非常函数。
24. 对象数组 Point A[2] = { Point(2,3) },部分初始化时,编译器会将其他部分自动补0.
25. {
int I; //内部auto变量
  Static int ii = &I; //错误,静态在编译时确定值,栈区运行时才有值
}
26. 访问数组的方法:   int A[10]; int *p = A;
A[i]    *(A+i)    *(p+i)    p[i]
  访问成员函数的方法:   p->G()    (*p).G()
27. 类中的每个成员函数都有一个隐含的this指针,待在形参的第一个位置。
 A a; a.G(); ------> void G(A *this); G(&a)
28. 析构函数为虚函数的原因: 当基类的指针指向派生类的对象时,如果非虚,基类的指针delete时,派生类中派生的那部分无法析构,造成内存泄露。
29. 默认拷贝构造函数和默认赋值函数,编译器会以“位拷贝”而非值拷贝的方式处理,若类中含有指针变量或引用变量,会发生“浅拷贝”或“浅赋值”。这种情况下需要自己定义拷贝构造函数和赋值函数。
30. 声明指向类的非静态成员的指针,要用“类名::”.
 指向成员的指针其实是个偏移量, Int Point::*p = &Point::x.
31. 对象成员的访问方式有八种:
(1). 对象名.公有成员;
(2). 类名::静态公有成员;
(3). 对象指针->公有成员;
(4). 对象名.*数据成员指针名;
(5). 对象指针->*数据成员指针名;
(6). (对象名.*函数成员指针名)(参数表);
(7). (对象指针名->*函数成员指针名)(参数表);
(8). 对象名.类名::被隐藏的公有成员
32. 堆空间的特点: 只能用指针,无名,运行时申请,长命,低效,需要初始化.
 New/delete会调用构造函数和析构函数;
Float (*cp)[9][8]; cp = new float[10][9][8];
Point &rp = *(new Point(2,3));   delete &rp;
Point *p[2] = { new Point(1,2), new Point(3,4)};
33. 类的聚集: 类的成员中含有指针和引用,引用了类外数据。
   组合时资源固定,聚集资源动态申请,很灵活。
34. 父类构造函数私有时,不能产生子类。
前置声明的类只能声明为引用或者指针成员,不能声明为对象,因为还未实现。
35. 打捞:
 继承改变了访问权限,“打捞”能够恢复访问权限
 Using 类型名::成员名;
 打捞不能区分重载,不能隔代打捞,只能还原不能提高访问权限,子孙类不能重复打捞.
36. 声明指向公有成员的指针:
  类型说明符  类名::*指针名
  类型说明符  (类名::*指针名)(参数表)
 对指向成员的指针赋值:
  指针名 = &类名::数据成员名;
  指针名 = 类名::函数成员名;
37. 类型兼容规则:  切割 或者  假切割。
38. 在公有继承中:
 派生类的对象可以替代基类的对象;
 派生类的对象可以被赋值给基类的对象;
 派生类的对象可以初始化基类的引用;
 指向基类的指针可以指向派生类对象。
39. 二义性
 (1). 同名二义(同名隐藏规则): 派生类与基类成员同名时,派生类成员将屏蔽基类中的成员;若要访问基类,需要用“类名::”;据继承方式不能区分二义性。
 (2). 路径二义(多继承中的多层共组): 解决方式: 虚基类,class B: virtual public A。
40. 最远派生规则:
虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。
41. 虚基类的构造函数调用规则:
 (1).产生对象的派生类首先调用虚基类的构造函数;
 (2).然后安装继承次序调用直接基类的构造函数;
 (3).如果有包含的对象,再按照声明次序调用所包含对象类的构造函数;
 (4).最后才执行函数体,普通类型数据成员被赋予初值。
(5).多继承时,访问权限就低不就高,即以最低访问权限为准。
42. 局部类与内嵌类:
 局部类: 函数体内定义的类,只有在该函数内用;必须采用隐含内联的方式定义,不能含有静态数据成员,常采用匿名类的方式. 典型应用是STL中的迭代器
 内嵌类: 类中定义一个新类,class B { class A {…} } , 最外层实现 B::A::fun() {…}
43. 多态的类型:
(1). 类型转换多态: 四个cast;
(2). 重载: 运算符重载和函数重载 ---> 静态多态;
(3). 虚函数--->动态多态;
(3). 模板: 函数模板和类模板;
44. 运算符重载:
(1). 运算符重载的两种形式: 重载为成员函数或者友元函数;重载不能为类属性即不能为静态,不能改变优先级、结合性、操作数个数,只能重载已有运算符;
 (2). 下列运算符不能重载: .  .*  ::  ?:  sizeof  typeid  #  || &&  ,(逗号)  四个cast;
 (3). 重载前++一定返回引用,重载后++一定返回对象
 Clock & operator ++(); //前++,返回引用是为了连续运算,如++++C
 Clock operator ++(int); //后++,参数仅识别重载,后++返回加之前的临时对象,而非*this
(4). 对象赋值出现赋值运算符连锁调用   X & X::operator = (X x);
重载函数调用运算符()  X(arg1,arg2) ---> X.operator()(arg1,arg2)
下标运算符重载[]   X[Y] ---> X.operator[](Y)
 (5). 重载为友元判断标准:  Complex C1;  5+C1---> 5.operator+(C1);//错误
45. 运算符重载的设计原则:
 (1).全部的单目运算符建议设计为成员函数,重载为成员函数参数会比原来少一个;
 (2).全部双面运算符建议设计为友元函数;
 (3). = () [] -> * 必须重载为成员函数;
 (4). >>  <<必须是友元函数;
 (5).虚函数建议设计为成员函数;
 (6).若允许链式运算,应返回本对象引用;
 (7).若定义了+ - * / %之一,千万别忘了定义“=”;
 (8).所有的赋值(如+=、<<=、|、&、=)建议为成员函数;
46. 隐式类型转换规则:
 (1). 算术表达式中,短服从长,简单服从复杂;
 (2). 赋值表达式中,右服从左;
 (3). 三目运算符中,?= 取表达式2和3中最大的那个;
 (4). 函数调用形实结合,实参服从形参;
 (5). 函数返回与函数类型不一致,返回值服从返回类型.
47. 类型转换函数:
 (1). 外--->内:构造函数  如,T t(5);  5--->T    t=5; --->t=T(5);
 构造函数承担了从外向内的类型转换,只用于带参构造函数;
explict关闭构造函数类型转换功能;
 (2). 内--->外:operator 类型名()
不写返回类型,不写参数,必须有return语句,可以被继承,可以是虚函数,类里可以有多个,该函数应该是类的非静态成员函数,为const更好。
48. (1).虚函数只能由指针或者引用调用,可以为private.
(2).**vptr 对象属性;  vtable为类属性,登记virtual函数;
(3).动态多态的前提: 公有继承,基类virtual,派生类重写,用指针或引用调派生类对象;
(4).子类虚函数表项至少和父类相等,包括虚析构;
(5).”重写”返回类型可允许”类型兼容”;
(6).父类void fun() const和子类的void fun()是”隐藏”关系,因为参数不同;
   (7).vptr也服从对齐原则,一个派生类有多个父类时,该对象会有多个vptr指向各自的vtable,多层共组则会合并;
49. 纯虚函数 virtual 类型 函数名(参数表) = 0;
 纯虚函数也可以有函数体,其函数体可被子类重写中调用,”=0”本质上使vtable对应位置为NULL;
 当类中包含至少一个虚函数的时候才声明虚析构函数,若想使一个类成为抽象类但刚好又没有任何虚函数,可以在想要成为抽象类的类中声明一个纯虚析构函数 virtual ~function() = 0;
50. 抽象类只能用于产生指针和引用,不能产生对象,只能当做基类,不能作为转换目标、参数、返回值。
51. 命名空间(逻辑概念):
 (1).Namespace NS{…},在命名空间外访问,要使用前缀。
 (2).匿名空间: namespace {…} 本文件域内无限制使用,但别的文件不可使用,相当于C语言”全局静态”; 头文件不编译,一个编译单元只可以有一个匿名空间,VC中一个.cpp是一个编译单元。
52. 命名空间的使用方法:
 (1). NS::File obj;    NS::Fun();
 凡用就要加,最小开放度  ---> 单个一次性开放;
 (2). using NS::File;         ---> 单个一劳永逸开放;
 (3). Using namespace std; ---> 全面开发,可能产生”名字冲突”,同名覆盖,外来服从内层。解决二义性用”命名空间::”区分。
 (4). 优先级:开放程度越小,优先级越高。
 (5). 名空间可用别名: namespace 新名 = 旧名;
 (6). 所有不带.h的头文件都在std中;所有带.h的头文件共18个,都是全局的;使用了using namespace std时不得再使用标准库中的.h头文件,因为它们不在std中;但自己定义的头文件可以使用.h。
53. RTTI(run-time type Identification)动态类型识别:
类型转换:
C风格:   (int)…         int(…)
C++风格: 四个cast
(1).const_cast<type> (object) 去掉常属性
String const B(“Hello World”);
String &b = const_cast<string &>(B);
(2). Static_cast<type>(object) 除以上3种cast以外,所有的都能转换,类似于C语言的”()”.
Double d = 12.34;  Int a = static_cast<int>(d);
(3).reinterpret_cast<type>(object) 用于转换指针,转换不安全,尽量少用。
Double d = 3.14;  int *p = reinterpret_cast<int *>(&d);
(4).dynamic_cast<type> (object) 运行时转换符
a.能将派生类的指针或引用转换为基类;
b.能将基类的指针或引用转换为派生类类型,不可用于对象;转指针失败返回NULL,转引用失败抛出异常std::bad_cast;
dynamic_cast<T*>(p); //把指针p转化为T类型,其中p必须为多态类型的指针
dynamic_cast<T&>(p);//若P不是T类型则抛出异常bad_cast
54. RTTI核心:  typeid运算符  typeinfo类
 <typeinfo>头文件常用的四个函数:
(1) operator ==;
(2).operator !=; 
 (3).返回被测对象类型名const char* name()const;
 (4).判断两个对象定义的先后关系 bool befer(…)const;
 typeid(对象名/类型名)返回一个记录着目标类的类型的typeinfo类的对象
55. 模板:参数类型化的技术
(1).函数模板: 功能相同但类型不同的函数制造模型
 Template <typename 标示符>
 模板函数式由函数模板生成的函数(编译时生成)
 Template <typename T>
 T abs (T x) { return x<0?-x:x; }
具现:  int n = -5; 
Cout <<abs(n)<<endl; //隐式实例化
abs<double>(5); //显示实例化
无参函数模板必须采用显示实例化;
模板函数也可以内联;
(2). 类模板: 相同属性和功能的类的模型,创建成员布局相同的多个类。
 类模板的模参可含有类型默认值:
 Template <typename T=int, unsigned long len = 100>
 Class MyArray { 
T m_A[len]; //编译完后Len已经为常量  
  Void push(T value);
};
每个成员函数实现时,无论是否使用到模参,都必须带模板头:
Template <typename T=int, unsigned long len = 100>
Void MyArray<T>::push(T value) { … }
类模板必须显示实例化:
 MyArray <bool, 32> ab;   MyArray <long> ab;  MyArray <> ab;
 模板参数默认值为模板:
 Template <class T, class Sequece = deque<T> >
 Class stack { Sequece container; };
 Stack <int> mystack1;
 Stack <int, list<int>> mystack2;
 Stack <int,stack<int>> mystack3;

56. 异常处理 :  try    throw   catch
Throw负责发现异常报警,产生并抛掷异常对象,函数声明中称为“异常接口声明”;
Try设置侦错范围,即保护段;
Catch 负责处理异常的捕获;
异常处理机制靠类型匹配的,异常对象建立在专用的异常栈上而非函数栈,因而可跨越函数传递;
不要将局部对象的地址或引用作为异常对象抛出;
Throw必须用if等判断语句,触发建立异常栈,生成异常对象;
Catch只要勉强匹配就会忽略后面,catch(…)为万能匹配,应该安排在最后面;
二次抛掷 throw
全局对象在编译时确定,不能被捕获异常;
异常接口声明,列出函数可能抛掷的异常:
Void Fun() throw (A, B, C, D) 表明可能抛四种异常;
Void Fun() 表名可能抛出任何类型的异常;
Void Fun() throw () 不抛掷任何类型的异常;
Catch的参数应该使用引用或者指针,而不是值传递,这样才可以启用多态;l
派生类成员函数的异常声明至少应该与基类异常一致。

57. 标准模板库STL
STL六大组件: 容器,迭代器,算法,函数对象(仿函数),空间配置器(allocator),适配器;
(1).容器: 七种基本容器,三种扩展容器
基本容器: Vector, deque, list, set, multiset, map, multimap前三个为线性,后四个为平衡二叉搜索树(红-黑树);
扩展容器: 栈 ,队列, 优先队列(priority_deque);
类容器/近容器: string, bitset, valarray, 数组;
引用不可以做容器的元素,但指针可以,因为引用创建时必须初始化,且容器仅支持对象语义;
双端队列deque: 顺序容器,支持下标运算,本质上是两头开放的数组,没有容量的概念;
关联容器: 逻辑上非线性,即无序,不需要预留空间,也不存在内存再分配,一般采用红黑树作为底层存储;不可以直接对关联容器的元素对象直接修改,使用const_iterator.
Set容器: 值是单一的,不允许有重复,元素是升序的,set<double, less<double>>doubleset(a,a+5);
Map容器: 一个个的key/value对-----pair对象;key不可重复,operator[]()返回key所对应的value;若key不存在则创建一个对象加入;
(2). 适配器: 使用组合或者聚集改造的别的类(7中基本容器)
Stack栈默认基于deque,还可以基于vector,list;
Queue 和priority_queue都在<queue>中;
(3). 函数对象<functional>15个: 行为类似函数的一种对象,解决了把函数当做值一样传递的问题。
任何普通函数和重载了operator()的类对象都满足函数对象的特征;
十五个函数对象分为算术运算、关系运算、逻辑运算三种;
(4). 迭代器: 面向对象版的指针,指向容器中元素的相对位置,和普通指针用法基本相同;
 分类:  输入迭代器
     输出迭代器
   前向迭代器
   双向迭代器
   随机访问迭代器
 容器的适配器不支持迭代器;
 所有的algorithms前两个参数都是一组iterators构成的[first,last)区间;
 迭代器的适配器:  back_insert_iterator
     Front_insert_iterator
     Insert_iterator
 逆向迭代器1个,修饰除前进迭代器以外的迭代器,vector<int>::reserse_iterator it;
 输出流迭代器2个: ostream_iterator
 输入流迭代器2个: istream_iterator
 迭代器失效的原因: 底层存储发生变化,造成迭代器失效的函数有:reserve(), resize(), erase(), clear(), insert(), sort(), copy(), replace(), remove(), unique()以及求交、并、差集的泛型算法;
 扩充迭代器功能的辅助函数有:
 Advance()迭代器位置增加  p+n
 Distance()    p1-p2
 Iter_swap()  元素交换

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值