C++ 基础知识汇总 持续更新
Posted yocichen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 基础知识汇总 持续更新相关的知识,希望对你有一定的参考价值。
摘录一些C++面试常考问题,写一些自己的理解,欢迎来摘果子。
static关键字
用于声明静态对象;
静态函数只在本文件可见。(默认是extern的)
全局静态对象:全局静态对象,存储在全局/静态区,作用域整个程序,在程序结束才销毁;
局部静态对象:在函数内部加上static声明的变量,在首次调用时初始化,然后一直驻留在内存,作用域是该函数,可用于函数调用计数(primary有例子),程序结束释放;
静态数据成员:归属于类,类对象共享,类外初始化,类对象可访问;
静态函数成员:归属于类,只能访问静态数据成员。
const 关键字
核心功能:限定只读
const T var; 声明常量,存储在常量区;
const T* p : 不可通过p指针修改对象值;T * const p : 常量指针,指针不可被赋值/地址不可改变。
const T function(const T, const T*, T* const, const T&) const &/&& …;
限定返回值为常量、常量形参、指针常量、常量指针、常引用、防止对象属性被改变(对象只读)STL源码大量使用
C/C++区别
C:面向过程语言、编译型(也可以实现对象化,但没有C++那样顺畅强大)。
C++:面向对象语言、编译型、拥有封装继承多态三大特性、支持泛型编程、模板。
引用指针区别
指针是对象的地址,而引用是对象的别名,均可改变对象值;
指针可以被重新赋值,引用初始化后不可以改变,就像私家车与共享汽车。
指针可以不知指向对象,引用必须初始化;
在实现层面,指针和引用完全相同,都占空间,汇编代码看一下就知道;
传指针和传引用的区别在于,会否发生拷贝,指针会发生拷贝,会降低效率,使用引用直接访问该对象,提高效率。
内存泄漏以智能指针,智能指针的内存泄漏,怎么解决?
动态申请的内存,未进行释放,比如new的空间,没有适时delete掉。导致内存泄漏,可用智能指针解决。
Auto_ptr可以实现部分shared_ptr的功能,但已经被弃用。
Shared_ptr:多指针共享对象,存在引用计数,赋值给其他指针,计数增加;被赋值,计数减少;当引用计数为0,自带的销毁函数调用类析构函数析构对象;当循环引用时,仍然会发生内存泄漏;靠weak_ptr解决。
Weak_ptr:弱引用,被复制shared_ptr对象不会引发引用计数,能很好的解决shared循环引用内存泄露的问题。
Unique_ptr:任意时刻只能由一个指着对象,禁止复制和拷贝,实现形式“=delete”。计数为0,析构对象。
数组和指针,野指针
C++内置数组是一块连续的内存空间,数组名其实是一个指针,指向数组第一个元素地址。野指针指向已删除对象地址或是其他未申请地址的指针,访问野指针很危险。
静态函数与虚函数(多态),虚析构
静态函数在编译时绑定;
虚函数由virtual标识,采用动态绑定方式。派生类覆盖基类虚函数,基类指针指向派生类对象,实现动态多态。在运行时判断指针指向的对象类型从而调用该对象的函数版本。
底层的实现是虚表。
基类析构函数必须为虚。否则若有基类指针指向派生类对象进行析构时函数调用基类析构函数,会出现错误,发生内存泄漏。
可以不为虚,只要你保证不会发生动态绑定。
函数指针
顾名思义,指向函数的指针。相当于拿到函数的访问地址,可以方便地将函数作为参数传入其他函数。。
定义方法:int (*fp)(int, int);
赋值:fp = &max;
直接调用:fp(1, 2)
作为参数使用 int cal(int a, int b, int (*fp)(int, int))
函数重载二义性
二义性是在编译阶段编译器进行函数匹配(匹配函数名、参数种类和个数)的时候出现的错误。多种情景会导致函数重载二义性。
默认参数与无参函数;
隐式类型转换造成的。比如:double, int, ßlong
类类型的转换,比如派生类对象指针/引用可以传给基类指针/引用,当出现派生类指针和基类指针两个函数时,编译器无法匹配,出现错误。
解决方法:①编程时注意;②使用explicit关键字声明函数,避免隐式类型转换的出现。
重载和覆盖
重载指的是函数名相同而参数个数/类型不同从而实现调用同名函数,给定不同参数实现静态多态。
覆盖多用再类继承中,派生类继承基类,然后实现自己的函数版本,如果是虚函数一般在后面加上关键字override。
隐式类型转换
种类较多。
① Int, double, float, unsigned_int, unsigned_long等内置类型可以相互转换;
② 整型提升,在计算时位数小于int的类型都会提升成为int进行计算;
③ 在表达式中,先将各个类型转换成类型中最宽的类型再进行计算,计算完成时将结果(右值)再转换成为左值变量的类型,然后抛弃将亡右值。
④ 基类与派生类之间存在隐式类型转换关系。向上(未理解)、向下、横向转换。派生类向基类进行转换,派生类向派生类进行转换。
四种cast类型转换
static_cast:用于一般的强制类型转换
const_cast:专用于除去const属性
dynamic_cast:RTTI的内容,用于运行时类型检查,将派生类对象指针/引用转换成基类指针/引用(upcast);或者将指向派生来对象的指针/引用转换成派生类对象指针/引用(downcast)。
interpret_cast:强制重新解释,即显示声明该变量/数据由该类来解释。
New/delete VS malloc/free
new/delete是C++关键字,操作的是对象,其实调用的是构造和析构函数。
malloc/free是C库函数,较为直接的操作内存,需要指定类型和空间大小。
new 有类型检查,malloc没有。
new返回内存地址,malloc返回void 指针,需要显示转换成我们需要的类型。
malloc函数原型:void* malloc(size_t) 头文件stdlib
RTTI
Runtime Type Identification,运行时类型识别,能够在运行时准确判断对象的类型,引入了type_info对象。通过typeid().name()来查看,头文件type_info.h。
另外类转换函数dynamic_cast<T>(expression),可以判断expression is a T问题。
可以进行upcast和downcast转换
upcast: 向上转换,即把派生类对象指针/引用转换为基类指针/引用;
downcast:向下转换,把指向派生类的基类指针/引用转换成为派生类指针/引用。
比较:type_info对象会增加开销。
虚函数的实现---虚表,怎么实现了多态?
虚表vftable,编译器为每个拥有虚函数的类都建有一张虚函数表,里面存有虚函数的入口指针(地址)。在类对象的内存布局中,先是一个vfptr虚表指针,指向虚表首地址,而后通过偏移量的形式来访问虚表中的地址。
发生单继承时,派生类内存布局,先是复制一份基类内存布局,然后是自己的布局(注意内存对齐)。虚表指针指向自己的虚表,派生类虚函数地址如果自己未覆盖,那么就是基类的,否则是自己的函数地址。
发生多继承时:先按照继承顺序,从左到右排布基类的布局包括虚标指针,然后排布自己的指针和数据;派生类虚表排布形式是按照继承顺序,是继承来的虚函数,如果有覆盖则换成自己的函数地址;然后是下一个基类,直至基类排布完毕。多张虚表各自独立。
发生虚继承时:无论是对象内存排布还是虚表,虚基类的部分都被放到最后排布。
以上是关于C++ 基础知识汇总 持续更新的主要内容,如果未能解决你的问题,请参考以下文章