
本篇是即 上一篇面经 之后的第二篇面经,涉及 C++ 在面试过程中常问道的知识点,也是笔者在面试过程中曾经被问到的题目,但是由于公司的不同,面试官的不同,问道的问题的深度也不一样,建议在复习基础知识的时候,首先拓展自己的宽度,当宽度足够了再加深自己的深度。举个非常简单的例子:面试官可能会问道多态是怎么实现的,开始复习的时候可能仅能回答上来“多态是通过虚函数来实现的”;那么更为理想的答案是在回答的过程中我们也简单把“虚函数的底层实现方式”简单描述下,这就体现出我们回答的问题是有深度的。
更新目录及链接:
🔗 2021 秋招面经总结 —— C++ 篇(一)
🔗 2021 秋招面经总结 —— C++ 篇(二)
参考链接:🔗 这里
类本身没有大小,这里类的大小是指:类对象所占的大小。
#include <bits/stdc++.h>
using namespace std;
class A{
//静态成员
static int a;
const static int b;
static int fun1(){}
//普通成员函数
void fun(){}
};
//普通数据成员
class B{
int a;
};
//虚函数
class C{
virtual int fun(){}
};
//结构体对齐原则
class D{
int a;
virtual int fun(){}
};
int main(){
cout << sizeof(A) << endl;//1
cout << sizeof(B) << endl;//4:表示一个整型变量的大小
cout << sizeof(C) << endl;//8:虚函数表的指针的大小
cout << sizeof(D) << endl;//16:整型变量的大小+虚函数指针的大小+对齐原则(4)
return 0;
}class A
{
public:
A(int x){}
};问:A a = 1;是否正确, 如果正确, 那么它调用了哪些函数?,
这里会进行隐式转化 A a(1).implicit
正确
这类题目更常见的是在基类和子类有不同实现方法。(虚函数相关,例子很多,不多说了)
(这个系列也很重要,建议侯捷老师的这方面的书籍与视频),其中包括内存管理allocator,函数,实现机理,多线程实现等,笔者也整理过STL相关的面经,有兴趣的可以自行查阅
hashtable 是采用开链法来完成的,(vector + list)
vector 是一个动态数组,底层实现是一段连续的线性内存空间。
扩容的本质:当 vector 实际所占用的内存空间和容量相等时,如果再往其中添加元素需要进行扩容。其步骤如下:
从 vector 扩容的原理也可以看出:vector 容器释放后,与其相关的指针、引用以及迭代器会失效的原因。
主要是在插入元素方面:插入元素需要考虑元素的移动问题和是否需要扩容的问题
频繁的调用 push_back() 也是扩容的问题对性能的影响
参考:🔗 这里
C++ 内存分区:栈、堆、自由存储区、全局/静态存储区、常量区
堆和自由存储区的区别:
参考链接:🔗 这里
指针和数组的对比:
char a[] = "hello";
a[0] = 'l';
cout << a << endl;
char *p = "world";//p指向常量字符串
p[0] = 'h'; //运行出错char a[] = "hello";
char b[10];
strcpy(b, a);//不能用b = a;
if(strcmp(a, b) == 0)//不能用a==b
cout << "endl";char a[] = "hello";
char *p = a;
cout << sizeof a << endl;//6字节
cout << sizeof p << endl;//8字节(64位)但是当数组作为参数传递时,数组会自动退化为指针
void fun(char a[100]){
cout << sizeof a << endl;//结果是8(64位)
}调用函数申请空间
void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100); // str 仍然为 NULL
strcpy(str, "hello"); // 运行错误
}上述程序出错的原因在于:调用函数时,传递的是变量的值“值传递”,在 GetMemory 函数内部改变的是参数 p 的副本,并不会影响参数 p 本身,也就是说 str 并没有获得内存。这样程序最终会导致内存泄漏,调用 GetMemory 函数多少次,就会出现多少次内存泄漏,因为在函数内部只动态申请了内存,并没有用 free 释放掉动态内存空间。
如果想要通过这种方式申请空间,将 GetMemory 函数的形参改为“指向指针的指针”
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
char *str = NULL;
GetMemory2(&str, 100); // 注意参数是 &str,而不是str
strcpy(str, "hello");
cout<< str << endl;
free(str);//特别注意:这里是将动态申请的内存空间释放掉
}char *GetString(void)
{
char p[] = "hello world";
return p; // 编译器将提出警告:将局部变量返回
}
void Test4(void)
{
char *str = NULL;
str = GetString(); // str 的内容是垃圾
cout<< str << endl;
}char *GetString2(void)
{
char *p = "hello world";
return p;//编译器给出警告:将string const转化成 char*
}
void Test5(void)
{
char *str = NULL;
str = GetString2();
cout<< str << endl;
}在一个函数中如何返回一个变量的指针?
对于普通变量而言,是存储在栈内存中,当函数调用完毕后,栈内存的空间会被释放,如果返回局部变量的指针,回到主调函数后,该指针是悬挂指针指向垃圾内存,如何来处理这种情况呢?
可以返回存储在堆内存或者全局区的变量的指针:
#include <iostream>
using namespace std;
int func1(int param)
{
int local_param = param;
return local_param;
}
//返回静态变量的地址
int* func2(int param)
{
static int local_param = param;
return &local_param;
}
//返回存放在堆上变量的地址*
int *func3(int param)
{
int *new_param = new int(param);
return new_param;
}
int main()
{
int *pNewVar = func3(3);
int *pVar = func2(2);
cout << *pVar << endl;//2
cout << *pNewVar << endl;//3
cout << func1(1) << endl;
return 0;
}出现野指针的情形:
深拷贝和浅拷贝带来的问题:
类中默认的拷贝构造函数和赋值构造函数都是浅拷贝,当类的成员变量中出现指针变量时,最好使用深拷贝,避免内存空间多次释放的问题出现。
内存泄漏:
内存泄漏常指的是堆内存泄漏,当然还包括系统资源的泄漏
参考链接:🔗 这里
野指针: 指针指向的内存空间已经释放掉
悬挂指针: 指针指向的内存空间由于中间改变了其指向,之前的内存空间已无法释放,个人认为是和内存泄漏。
感觉二者并没有什么区别!!!!
野指针和悬挂指针都是指向垃圾内存的
本篇中知识点小结: