牛客网上的C++面经有很多重复的问题,经过我自己的理解和思考重新整理一下,希望可以加深自己的记忆。
静态全局变量:静态存储区,未初始化会初始化为0,在整个程序运行期间都存在,只能在当前文件可见(准确的说是定义开始到文件结尾)
静态局部变量:静态存储区,未初始化会初始化为0,作用域不变,当定义它的函数和语句块结束时,不会被销毁,一直存在于内存当中,只是不能访问,直到函数再次调用,值不变。
静态函数:只作用于当前CPP,其它CPP中同名的函数不会冲突。
类的静态成员,可以实现多个对象的数据共享,静态成员不属于某个对象而是属于整个类。
类的静态函数:与静态成员一样不是类的对象成员,因此引用静态成员不需要用对象名,静态函数无法直接引用对象成员,需要加对象名,但可以引用类中说明的静态成员,通过类名来调用。
设计思想上:
C++是面向对象编程
C是面向过程编程
语法上:
C++具有封装,多态和继承三种特性
C++多了很多类型安全的功能
C++支持泛型编程,有模板函数还有模板类
Const_cast:把const变量转换成非const。
Static_cast:用于各种隐式转换,比如非const转const,void*转指针等。
Dynamic_cast:用于动态转换。只能用于含有虚函数的类向上或向下转换,比如父类转子类,子类转父类。
Reinterpret_cast:几乎什么都可以转,但是可能出问题,不推荐使用。
为什么不用C的强制转换?
C的强制转换,转化不明确,不能错误检查,容易出错。
指针在内存当中有自己的一块空间,引用只是一个别名,
指针可以被初始化为NULL,而引用必须被初始化且有一个已知的变量,
作为参数传递时,指针需要解引用,而引用可以直接操作。
指针可以多级指针,但引用只有一级,
指针指向的对象可以改变,而引用不可以,
指针++和引用++不一样
如果返回动态内存分配的对象或者内存,必须用指针,引用有可能引起内存泄漏。
智能指针的作用是管理一个指针,因为智能指针是一个类,在作用域结束的时候会自动调用析构函数,析构函数会自动释放资源,从而避免内存泄漏。
Auto_ptr(C11已经抛弃):采用所有权模式。
Unique_ptr:用来代替auto_ptr,实现独占式,保证同一时间只有一个智能指针可以指向该对象。
Shared_ptr:共享式拥有,多个智能指针指向同一个对象,有独特的计数机制,当计数为0时会释放所指向的对象。
Weak_ptr:是一种不控制对象生命周期的智能指针,用于辅助shared_ptr,解决互相引用时发生的死锁问题,weak_ptr不会使引用计数增加或减少。和shared_ptr之间可以互相转化。
有的,当两个对象互相用一个share_ptr指向对方的时候,会造成循环引用,这时候计数会重复,在释放的时候计数没法降到0,以致没有调用析构函数导致内存泄漏,这时候就需要使用weak_ptr来避免这种情况发生了。
a余2判断余数是否为1,或直接与0x0001,因为二的倍数二进制的第一位为0,与0x0001==0。
逐位除十取余判断。
指针:保存数据的地址,间接访问数据,通常用于动态的数据机构,通过malloc分配内存,free释放内存,通常指向匿名数据,操作匿名函数。
数组:保存数据,直接访问数据,通常用于固定数目且数据类型相同的元素,隐式的分配和删除,自身即为数据名。
野指针就是指向一个已删除的对象或者未申请访问受限内存区域的指针。
将可能被继承的父类的析构函数设置为虚函数是为了避免在使用多态的时候,new一个子类然后用父类指针指向子类的时候能够调用子类的析构函数,避免内存泄漏。
C++默认的析构函数不是虚函数,是因为虚函数需要建立虚函数指针以及虚函数表,对于不需要被继承的类来说增加了内存的开销。
函数指针是一个指向函数的指针变量,在编译的时候,每个函数都有一个入口地址,该入口地址就是函数指针指向的地址,可以用该指针变量调用函数和做函数的参数,比如回调函数。
创建一个和当前进程映像一样的新进程,两个进程都会继续运行,在父进程中fork会返回子进程的pid,在子进程中会返回0,如果出现错误会返回负值。
析构函数与构造函数相对应,当对象结束生命周期的时候,会自动调用析构函数。
析构函数的名字与类名相同只是前面是个~号,以便于区分构造函数,析构函数不带任何参数,不带返回值,不能重载。如果自己没有定义析构函数,编译系统会自动生成一个缺省的析构函数。
如果类里面有指针,且在使用的过程中使用了动态分配的内存,则需要显式的在析构函数中释放掉申请的内存空间,避免内存泄漏。析构函数的调用顺序与构造时相反。
静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候进行动态绑定。虚函数因为用了虚函数表的机制所以每次调用的时候都会增加一次内存消耗。
重载:主要作用在类里面,两个函数名字相同,但是函数参数的个数类型不一样。
重写:子类继承父类,父类中的函数是虚函数,子类重新定义这个虚函数。通过父类指针指向子类来实现同一函数名,不同子类不同功能。
Strcpy:字符串拷贝,会把字符串从头一直拷贝到’\0’,因为没有指定长度,所以可能导致越界的情况,安全版本为函数strncpy。
Strlen:计算字符串的长度,返回从开始到’\0’之间的字符个数。
多态分为静态多态和动态多态,静态多态也就是重载,在编译的时候就已经确定运行的时机,而动态多态主要是靠虚函数来实现的,在运行的时候进行动态绑定,根据虚函数指针来确定实际运行的函数。
虚函数:虚函数的实现依靠虚函数指针和虚函数表机制,在父类将函数设置为virtual,在编译时会在类的头部建立一个虚函数指针指向一个虚函数表,子类重写虚函数的时候会替换虚函数表中的对应的函数地址。使用虚函数会增加额外的内存开销,降低效率。
一个先自增再返回,一个先返回再自增。
实现:
Int &int::Operater ++( )
{
*this+=1;
Return *this;
}
Const Int &int::operator++(int)
{
Int Oldvalue = *this;
*this+=1;
Return oldvalue;
}
__attribute((constructor))void before()
{
}
Const char *arr = “123”;//字符串123保存在常量区,const本来是修饰arr指向的值不能通过arr修改,但字符串123在常量区,加不加const都一样
Char *brr = “123”;//和arr指向同一个位置,同样是不能通过brr去修改”123”的值
Const char crr[] =”123”;//这里123本来在栈上,但是加了const修饰,编译器可能会做某些优化将其放在常量区。
Char drr[]=”123”;//将字符串123复制过来放在栈区,可以通过drr进行修改。
常量在C++中的定义就是一个top-level const加上对象类型,常量定义必须初始化。局部对象常量放在栈区,对于全局对象常量放在静态存储区,字面常量放在常量存储区。
Const修饰的成员函数表明函数调用不会对对象作出任何修改,如果确认不会对对象进行修改最好加上const修饰,这样的话无论是普通对象还是const对象都能调用该函数。
例如,低精度的变量给高精度变量赋值就会发生隐式类型转换。
默认是1M,不过可以调整
C++调用C函数需要用到,因为C语言没有函数重载。
New/delete是C++的关键字,不用指定内存大小,返回对应指针,对于类对象还会调用构造函数和析构函数;
malloc/free是C的库函数,使用的时候必须指明申请的内存空间大小,返回void*,必须强制转换,不会调用构造函数和析构函数。
(不太了解是不是真的这样)
每一个函数调用的时候都会分配函数栈,在栈内进行函数执行过程。调用前先把返回地址压栈,然后把当前函数的esp指针压栈。
从右到左
不可以,因为这种情况之下,调用构造函数,要将实参传给形参,这个过程中又会调用拷贝构造函数形成死循环。