C++重点知识整理

一、指针和引用的区别

1、32位平台下,指针是4个字节,而引用的字节数与其引用对象本身有关系。
#include<iostream>
using namespace std;
int main()
{
	int a = 10;
	char b = 'a';
	int *p1 = &a;
	char *p2 = &b;
	int &q1 = a;
	char &q2 = b;
	cout<<sizeof(p1)<<" "<<sizeof(p2)<<endl; //4  4 
	cout<<sizeof(q1)<<" "<<sizeof(q2)<<endl; //4  1
}
2、引用要进行初始化,并且一经初始化无法再改用,而指针可以更换指向,引用的底层实现是const *;
3、指针要配合*使用,才能取值,而引用则不需要,直接自带解引用功能;
4、指针存在多级指针,而引用只有一级引用(在C++11标准中,支持二级引用)。
int main()
{
	int a = 10;
	int b = 20;
	int *p1;
	//int &q1; error because:引用必须要初始化
	int &q1 = a;
	//&q1 = b; error because:一经引用不可更改
	p1 = &a;
	p1 = &b;//ok 指针是OK的

	int **pp1 = &p1;//存在多级指针 不存在多级引用
	cout<<*p1<<endl; //20  指针要配合*使用,才能取值
	cout<<q1<<endl;  //10  引用则不需要,直接就可以解引用
	return 0;
}
5、从汇编角度来看,定义引用和指针的汇编是一模一样的,都是先把该变量的地址放到寄存器里面,再把寄存器的值交给指针或者引用变量的地址里。
int *p1 = &a;
00B44EF9  lea         eax,[a]  
00B44EFC  mov         dword ptr [p1],eax  	
int &q1 = a;
00B44F05   lea             eax,[a]  
00B44F08   mov             dword ptr [q1],eax  
使用指针和引用的汇编也是一样一样的,先从变量中取地址值,然后根据地址值找到里面存放的值。
cout<<*p1; //20  指针要配合*使用,才能取值
01083604  mov         esi,esp  
01083606  mov         eax,dword ptr [p1]  
01083609  mov         ecx,dword ptr [eax]  
0108360B  push        ecx  
0108360C  mov         ecx,dword ptr ds:[1091380h]  
01083612  call        dword ptr ds:[1091384h]  
01083618  cmp         esi,esp  
0108361A  call        __RTC_CheckEsp (0108132Ah)  
	cout<<q1;  //10  引用则不需要,直接就可以解引用
0108361F  mov         esi,esp  
01083621  mov         eax,dword ptr [q1]  
01083624  mov         ecx,dword ptr [eax]  
01083626  push        ecx  
01083627  mov         ecx,dword ptr ds:[1091380h]  
0108362D  call        dword ptr ds:[1091384h]  
	cout<<q1;  //10  引用则不需要,直接就可以解引用
01083633  cmp         esi,esp  
01083635  call        __RTC_CheckEsp (0108132Ah)  

6、指针和引用的自增(++)运算意义不一样(指针移动的是地址,引用移动的是对象本身)

二、函数重载

函数重载是怎么回事呢?
因为C/C++都是编译型的语言,那么,就会进行符号表的生成,合并和解析等等,而C语言中的符号定义是由函数名构成,C++中的符号定义是由函数名称和参数列表决定,所以说,C++便可以构成函数重载,它的特性就是:多个函数:
1、函数名相同,参数列表不同;(返回值不用作为参考)
2、处在同一个作用域下;
3、如果参数列表中有const,则要小心。
(const不修饰指针的情况下,和原来类型视为一致:例如:const int 和int)
补充:关键词:volitale int 和int也不能构成函数重载。
volitale的作用:(1)、防止编译器对指令顺序进行优化调整;(2)、可以防止多线程对共享变量进行缓存,但是它并不能保证对数据的原子性操作。(防止cpu对指令的调整:barrier()函数)
对原子操作的保护可以有以下几种方法:互斥锁、读写锁、自旋锁、信号量;

三、内存泄露/资源泄露的情况?

1、malloc没有free掉,new没有delete掉
2、发生浅拷贝对象默认赋值的时候,你把你的给我,然后咱两指向同一个,然后我本身的内存就泄露了;
3、基类指针指向派生类对象,而析构函数未定义成虚函数,则会导致派生类的析构函数无法调用;
4、僵尸进程未处理,丢失了进程的内核栈;
5、打开的文件没有close掉;
6、new 了一个数组,delete没写[];如果是自定义类型,则只析构了一个对象,其余99个对象都没有调用析构函数。
7、智能指针的交叉引用;
8、malloc开辟失败,则会返回NULL;new开辟失败,则会抛出一个bad_alloc异常;所以说,如果构造函数中抛出异常,则会导致析构函数无法调用,则会造成内存泄露;
9、链表的节点丢失;

四、C++多态的原理

1、静多态:只要通过重载和模板实现。
2、动多态:只要通过虚函数实现。当基类指针指向/引用派生类对象时,有虚函数的类就会生成一个虚表,这个虚表存在只读数据段内,对象一经创建,则前四个字节存的就是虚表地址,然后当这个对象访问函数的时候,就会通过这个地址找到虚表,虚表的实现一般是同名覆盖,只要子类实现的函数就会覆盖父类的,所以就可以通过虚表里面的函数地址,找到真正的函数。

五、早绑定/晚绑定?静态绑定和动态绑定?

早期绑定是指在函数的入口地址已经在编译时全部计算完整,操作系统装入程序直接装入程序计算函数的入口地址,所以,在编译阶段决定执行哪个同名的被调用函数,这叫做静态绑定。
晚期绑定则是指在运行阶段才决定执行哪个函数,又叫动态绑定。其实现便是虚函数,如上题。
阅读更多
个人分类: C++
上一篇指针和数组的区别
下一篇TCP和UDP的那点事儿
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭