STL相关:https://github.com/huihut/interview/tree/master/STL
1.static关键字
2.sizeof和strlen区别
char *p = "abcdef";//字符串
char arr1[] = "abcdef";//字符串
char arr2[] = {'a','b','c','d','e','f'};//6个字符。没有\0
sizeof(p) = 8 ; // 指针大小为8字节(64位系统)
sizeof(arr1) = 7; //arr1为数组名,计算数组大小 + 末尾'\0'
sizeof(arr2) = 6; //和arr1区分,该情况末尾没有'\0'
strlen(p) = 6;
strlen(arr1) = 6;
strlen(arr2) = 不确定; //没有'\0',无法确定结束
char *p = "abcdef0\0a";//字符串
char arr1[] = "abcdef0\0a";//字符串
char arr2[] = {'a','b','c','d','e','f','0','\0','a'};
sizeof(p) = 8; //指针大小为8字节(64位系统)
sizeof(arr1) = 10; //'\0'算一个字符,加上末尾'\0'10个字符
sizeof(arr2) = 9; //没有结尾'\0'
strlen(p) = 7; //strlen均计算到'\0'之前
strlen(arr1) = 7;
strlen(arr2) = 7;
3.struct和class的区别
4.malloc和new的区别
5.指针和引用的区别
6.extern关键字
7.(1)define和typedef区别
特别注意typedef和define在处理指针的时候的问题,如:
typedef int* pInt1;
#define pInt2 int*;
pInt1 a,b; //等价于 int *a; int *b; 定义了两个整型指针变量
pInt2 a,b; //等价于 int *a, b; 定义了整型指针a和整型变量b
(2)define和内联函数(inline)区别
inline关键字的作用:
在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。使用 inline 修饰带来的好处我们表面看不出来,其实,在内部调用的地方都会进行替换,这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗;
8.条件编译#ifdef, #else, #endif作用?
9.常引用 const typename & refname = varname
10.指针常量和常量指针
11.区别以下指针类型
12.数组名和指针的区别
13.野指针
14.堆和栈的区别
15.volatile关键字
16.面向对象三大特性
17.public/protected/private区别
18.C++空类
19.构造函数能否为虚函数,析构函数呢?
虚析构函数作用:
构造函数不能是虚函数的原因:
20.虚函数和纯虚函数的区别
虚函数是为了实现动态编联产生的,目的是通过基类类型的指针指向不同对象时,自动调用相应的、和基类同名的函数(使用同一种调用形式,既能调用派生类又能调用基类的同名函数)。虚函数需要在基类中加上virtual修饰符修饰,因为virtual会被隐式继承,所以子类中相同函数都是虚函数。当一个成员函数被声明为虚函数之后,其派生类中同名函数自动成为虚函数,在派生类中重新定义此函数时要求函数名、返回值类型、参数个数和类型全部与基类函数相同。
纯虚函数只是相当于一个接口名,但含有纯虚函数的类不能够实例化。
21.重载,覆盖和隐藏的区别
22.构造函数/析构函数调用顺序
成员类对象的构造函数:如果类的变量中包含其他类(类的组合),需要在调用本类构造函数前先调用成员类对象的构造函数,调用顺序遵照在类中被声明的顺序
23.深拷贝和浅拷贝
24.什么是虚指针
虚指针或虚函数指针是虚函数的实现细节。
25.this指针是什么?
this指针是类的指针,指向对象的首地址。
this指针只能在成员函数中使用,在全局函数、静态成员函数中都不能用this。
this指针只有在成员函数中才有定义,且存储位置会因编译器不同有不同存储位置。
26.C++字节对齐
字节对齐的作用:
struct字节对齐规则:
struct temp
{
static int A;
char B;
double C;
int D;
}a;
sizeof(a) = 1 + 7 + 8 + 4 + 4 = 24;
注意:
#pragma pack(n)用来自定义字节对齐方式
static静态变量其存放位置与结构体或类的实例无辜眼,不对结构体大小产生任何影响;
union字节对齐规则:
例如:
union
{
char x[5];
int i;
}a;
int main()
{
a.x[0] = 10;
a.x[1] = 1;
sizeof(a) = 8; //union分配的内存必须是union中所有数据类型的整数倍,所有是1和4的倍数
//因为char[5]占5个字节,所以union内存大小为8个字节
}
27.C++内存管理
在C++中内存分为5个区,分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
还有一种说法:栈,堆,全局/静态存储区, 常量区,代码区(存放函数体的二进制代码)
28.编译的四个步骤
1.预处理阶段(.c -> .i)
2.编译阶段 (.i->.s)转换为汇编语言文件
3.汇编阶段 (.s->.o) 得到机器语言(二进制)
4.链接阶段
各个源代码模块独立的被编译,然后将他们组装起来成为一个整体,组装的过程就是链接。被链接的各个部分本本身就是二进制文件,所以在被链接时需要将所有目标文件的代码段拼接在一起,然后将所有对符号地址的引用加以修正。
静态链接:
目标文件概念:
静态链接器以一组可重定位目标文件为输入,生成一个完全链接的可执行目标文件作为输出。
链接器主要完成一下两个任务:
静态库有以下两个问题:
动态链接:
共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中通常用 .so 后缀来表示,具有以下特点:
动态库运行时会先检查内存中是否已经有该库的拷贝,若有则共享拷贝,否则重新加载动态库(C语言的标准库就是动态库)。静态库则是每次在编译阶段都将静态库文件打包进去,当某个库被多次引用到时,内存中会有多份副本,浪费资源。
动态库另一个有点就是更新很容易,当库发生变化时,如果接口没变只需要用新的动态库替换掉就可以了。但是如果是静态库的话就需要重新被编译。
不过静态库也有优点,主要就是静态库一次性完成了所有内容的绑定,运行时就不必再去考虑链接的问题了,执行效率会稍微高一些。
29.explicit关键字
30.C++ 智能指针
智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。标准库提供的两种智能指针的区别在于管理底层指针的方法不同,shared_ptr允许多个指针指向同一个对象,unique_ptr则“独占”所指向的对象。标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象,这三种智能指针都定义在memory头文件中。(C++11弃用了auto_ptr)
循环引用问题
unique_ptr pInt(new int(5));
unique_ptr pInt2 = std::move(pInt); // 转移所有权
//cout << *pInt << endl; // 出错,pInt为空
cout << *pInt2 << endl;
unique_ptr pInt3(std::move(pInt2));
31.RAII机制
RAII是Resource Acquisition Is Initialization的简称,是C++语言的一种管理资源、避免泄漏的惯用法。利用的就是C++构造的对象最终会被销毁的原则。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。
32.C++常用新特性
C++11新特性
33.map和unordered_map的区别
对于需要高效率查询的情况,使用std::unordered_map容器。而如果对内存大小比较敏感或者数据存储要求有序的话,则可以用std::map容器。如果需要内部元素自动排序,使用map,不需要排序使用unordered_map
34.C++四种强制类型转换
注:上行转换:派生类——>基类; 下行转换:基类——>派生类
总结:
基本类型转换用static_cast
去指针或引用的const属性用const_cast
多态类之间的类型转换用dynamic_cast
不同类型的指针类型转换用reinterpreter_cast
35.C++成员函数在内存中的存储方式
每个对象所占用的存储空间只是该对象的数据部分(虚函数指针和虚基类指针也属于数据部分)所占用的存储空间,而不包括函数代码所占用的存储空间。
例如以下代码:
class D
{
public:
void printA()
{
cout<<"printA"<printA();
d->printB();
}
以上代码,输出“printA”后,程序崩溃。
类中包括成员变量和成员函数。new出来的只是成员变量,成员函数始终存在,所以如果成员函数未使用任何成员变量的话,不管是不是static的,都能正常工作。需要注意的是,虽然调用不同对象的成员函数时都是执行同一段函数代码,但是执行结果一般是不相同的。不同的对象使用的是同一个函数代码段,它怎么能够分别对不同对象中的数据进行操作呢?原来C++为此专门设立了一个名为this的指针,用来指向不同的对象。
C++程序的内存格局通常分为四个区:全局数据区(data area),代码区(code area),栈区(stack area),堆区(heap area)(即自由存储区),常量区。全局数据区存放全局变量,静态数据和常量;所有类成员函数和非成员函数代码存放在代码区;为运行函数而分配的局部变量、函数参数、返回数据、返回地址等存放在栈区;
在类的定义时,类成员函数是被放在代码区,而类的静态成员变量在类定义时就已经在全局数据区分配了内存,因而它是属于类的。对于非静态成员变量,我们是在类的实例化过程中(构造对象)才在栈区或者堆区为其分配内存,是为每个对象生成一个拷贝,所以它是属于对象的。
静态成员函数和非静态成员函数都是在类的定义时放在内存的代码区的,因而可以说它们都是属于类的,但是类为什么只能直接调用静态类成员函数,而非静态类成员函数(即使函数没有参数)只有类对象才能调用呢?原因是类的非静态类成员函数其实都内含了一个指向类对象的指针型参数(即this指针),因而只有类对象才能调用(此时this指针有实值)。
需要说明,不论成员函数在类内定义还是在类外定义,成员函数的代码段都用同一种方式存储。不要将成员函数的这种存储方式和inline(内联)函数的概念混淆。不要误以为用inline声明(或默认为inline)的成员函数,其代码段占用对象的存储空间,而不用inline声明的成员函数,其代码段不占用对象的存储空间。不论是否用inline声明(或默认为inline),成员函数的代码段都不占用对象的存储空间。用inline声明的作用是在调用该函数时,将函数的代码段复制插人到函数调用点,而若不用inline声明,在调用该函数时,流程转去函数代码段的入口地址,在执行完该函数代码段后,流程返回函数调用点。inline与成员函数是否占用对象的存储空间无关,它们不属于同一个问題,不应搞混。
单例模式有以下特征:
1.懒汉式单例模式:在类加载时不初始化、直到用到才会初始化、以时间换取空间模式
class Singleton {
private:
Singleton() {}
~Singleton() {}
static Singleton* pInstance;
public:
static Singleton *GetInstance() {
if(pInstance == nullptr)
pInstance = new Singleton();
return pInstace;
}
static Singleton *Destroy() {
delete pInstance;
pInstance = nullptr;
}
};
线程安全的懒汉式单例的实现:
class Singleton {
private:
Singleton() {}
~Singleton() {}
static Singleton* pInstance;
public:
static Singleton *GetInstance() {
if(pInstance == nullptr) {
Lock();
if(pInstance == nullptr)
pInstance = new Singleton();
UnLock();
}
return pInstace;
}
static Singleton *Destroy() {
delete pInstance;
pInstance = nullptr;
}
};
2.饿汉式单例模式: 在类加载时就完成了初始化,所以类加载比较慢、获取对象的速度快、以空间换取时间模式、线程安全
class Singleton {
private:
Singleton() {}
~Singleton() {}
public:
static Singleton* GetInstance() {
static Singleton* pInstance;
return &pInstance;
}
};