一、类型转换
1、const_cast:
编译时完成,不能去除变量本身的const、volatile属性。可以作用于指针/引用,修改指针/引用的属性,达到间接修改的目的。
问题:const_cast修改值后,可能会被编译器优化导致错误。
void const_cast_T1(){
const int a = 1;
int &p = const_cast<int&> (a);
p = 10;
cout<<a<<endl<<p;
}
此时,输出a为1,p为10,这是因为因为编译器的优化,a没有从内存取值,而是直接读的内存器的值。(换成指针也一样)。
void const_cast_T2(){
volatile const int a = 1;
int &p = const_cast<int&> (a);
p = 10;
cout<<a<<endl<<p;
}
加上volatile属性,防止编译器优化,此时a与p输出值都为10。
2、static_cast
编译时完成,没有类型检查,不能去掉指针/引用的const与volatile属性。
可以进行上行转换(子类指针/引用转换为基类指针/引用),下行转换可以,但是不安全。
3、dynamic_cast
运行时完成,有类型检查,只能用于子类与基类之间指针/引用的上下行转换。转换失败会返回空指针,是安全的。
使用动态转换时,基类中必须有虚函数,否则编译不通过。
一般什么时候会向下转型?子类指针/引用向上转型为基类指针/引用后,想调用子类的成员方法,需要转型回来。
4、reinterpret_cast
什么都可以转,少用,不安全。一般用于指针类型转换,告诉编译器如何去解释内存中的数据。
5、为什么不用C的强制转换?
没有类型检查,不够安全。
二、指针与引用的区别
1、空间、大小、初始化:指针是存储类型为地址的变量,会为其分配空间并初始化为NULL指针大小为4/8; 引用是变量的别名,不会为其分配空间,必须是一个已有对象的引用。编译器编译后与被引用对象一样。引用的大小与被引用对象一样。
2、作为参数:
3、多级:有多级指针,没有多级引用。
4、运算符:指针++表示内存中递增;引用++表示运算。
5、内存泄漏:动态分配内存时,使用引用可能造成泄露。
泄露的原因在于疏忽,误以为引用是变量的引用。下列代码可正常释放内存:
void delete_test(){
int & p = *new(int);
delete &p;
}
6、可变性:指针可以改变指向的内存,引用一旦初始化不可改变。
7、const属性:指针有顶层const,引用没有顶层const。
引用不能改变。没有引用的引用。
三、智能指针
C++11支持的三种智能指针:shared_ptr,weak_ptr,unique_ptr。
1、为什么使用智能指针:防止内存泄漏,智能指针可以帮助回收内存。
2、智能指针通过什么管理内存:析构函数。
3、智能指针一定安全吗:不一定。循环引用也会导致内存泄漏。解决办法:引入weak_ptr。
4、三种类型的指针:
unique_ptr:严格独占,保证同一时间只有一个智能指针指向该对象;如果将一个临时右值的u_ptr赋值给另一个时,编译器将允许这么做。unique_ptr禁用了拷贝与赋值,没有引用计数。可以通过move函数实现转移。
shared_ptr:通过引用计数实现共享。
weak_ptr:可以从一个shared_ptr或者weak_ptr构造。不控制对象生命周期,它的构造与析构不会引起引用计数的增加或者减少,用于解决shared_循环引用引起的死锁。不可以通过weak指针访问对象的方法。必须通过lock()函数转化为share_ptr。
5、智能指针指向栈内存会发生什么?编译没有问题,运行时会崩溃。
四、数组与指针的区别
指针 | 数组 |
保存地址 | 保存数据 |
间接访问 | 直接访问 |
五、什么是野指针
野指针是指指针访问的位置不可知。
1、 指针指向一个已经删除或者释放的对象。
2、指针初始化时没有赋值或没有赋值为NULL,此时指针是随机值,此时指向的对象可能未被申请或者访问受限。
3、数组访问越界。
六、fork()函数
创建一个与当前进程一模一样的副本。如Linux的命令行,将会指向fork()函数。
七、析构函数
对象结束生命周期时调用。
调用顺序:1)派生类本身析构函数 2)派生类成员析构函数 3)基类析构函数
八、静态函数与虚函数区别
静态函数编译时已经确认运行机制,虚函数进行动态绑定。虚函数由于虚函数表机制,会增加内存开销。
九、strlen()与strcpy()
strlen计算字符串的长度,返回从开始到‘\0’之间字符的个数。(不包括结束符)
char* strcpy(char* d,const char *s)。由于参数不包括长度,存在越界隐患。安全版本为strnlen()。
十、++i与i++
++i:
int& operate++{
*this+=1;
return *this;
}
i++:通过是否输入参数重载。
为什么返回const?为了静止i++++操作。
const int int::operate++(int){
int temp = *this;
++(*this);
return temp;
}
十一、c++函数默认栈空间大小
1M,可调整。
十二、库函数与关键字的区别?
库函数是封装好的函数。
关键字是该编程语言已经“占用”的词语,自己的程序里不能再把它定义成其他用途。关键字有可能是声明语句,也有可能是流程控制语句,也有可能是一部分内置函数。
十三、RTTI
run-time Type Identification,运行时类型识别。dynamic_cast和虚函数会用到。
十四、typeid
返回指针实际指向的对象类型。
十五、C语言如何实现函数调用
每一个函数调用会为其分配函数栈,在栈内进行函数执行过程,调用前,先将返回地址压栈,然后把当前函数的esp指针压栈。
十六、函数参数压栈顺序
从右到左。
十七、拷贝构造函数的形参不能进行值传递
将实参拷贝给形参也要调用拷贝构造函数。形成了循环。