因为有道云笔记出了些问题,暂时先在这个上面记录,之后每次的笔试以及面试,当天一定要在当天就完成回顾,不然后面肯定会来不及去补充。这些天一天必须保持2-3题的刷题量。
C++的多态,这个知识点,早就应该掌握
百度 MEG-QA部门 测试开发工程师 一面面经(60min_笔经面经_牛客网 从这个面经里看到的面试问题,
语言相关:C++的多态,多态如何实现,实现过程中要注意什么,纯虚函数,new delete,
C++的内存分配方式(哪些分配在堆上,哪些分配在栈上)
查询sql相关的:表1存了学生学号和姓名,表2存了学生学号和成绩,查询一下成绩前10名的学生的姓名
无序数组求出其中最长连续数字的个数 ,
1.之前被问到的C和C++的区别
面向过程的思路:分析解决问题所需的步骤,用函数把这些步骤依次实现。 面向对象的思路:把构成问题的事务分解为各个对象,描述某个事务在解决整个问题步骤中的行为。??面向对象在分析和解决问题的时候,将涉及到的数据和数据的操作封装在类中,通过类可以创建对象,以事件或消息来驱动对象执行处理。
2.栈区放的是什么?
C++内存分区:栈 堆 全局/静态存储区 常量存储区 代码区
栈:存放函数的局部变量,函数参数,返回地址等,由编译器自动分配和释放
堆:动态申请的内存空间,就是由malloc分配的内存块,由程序员控制它的分配和释放,如果程序执行结束还没有释放,操作系统会自动回收。
全局区/静态存储区:存放全局变量和静态变量,程序运行结束操作系统释放
常量存储区:存放的是常量,不允许修改,程序运行结束自动释放
代码区:存放代码,不允许修改,但可以执行。
#include <iostream>
using namespace std;
int g_var=0;//全局区
char *gp_var;//全局区
int main()
{
int var;//栈区
char *p_var;//栈区
char arr[]="abc";//arr为数组变量在堆区 abc在常量存储区
char *p_var1="123456";//在堆区
static int s_var=0;//静态变量,在静态存储区
p_var=(char *) malloc(10);//由malloc分配的,在堆区
free(p_var);
return 0;
}
在C语言中,关于内存管理的知识点,先简单的复习一下几个基本概念
1)变量
- 全局变量 出现在代码块{}之外的变量就是全局变量
- 局部变量 (auto)一般情况下代码块{}内部定义的变量就是自动变量。对于局部变量,如果不进行初始化,那么它的初始值是随机的。局部变量定义在函数内部,其存储空间是动态分配在栈上的。函数被调用时,栈上会分配出一部分空间存放该函数中的局部变量(包括参数),
- 静态变量 是指内存位置在程序执行期间一直不改变的量,用关键字static修饰。代码块内部的静态变量
C语言知识整理(3):内存管理(详细版) - mattran - 博客园
extern的基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到次变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。
C语言中的函数默认都是全局的,可以使用static关键字将函数声明为静态函数(只能被定义这个函数的文件访问的函数)
再来看一个例子,帮助理解内存四区:
在执行一个C语言程序时,此程序将拥有唯一的“内存四区”——栈区、堆区、全局区(里面细分有一个常量区)、代码区. 【C代码】函数执行时的内存四区(附图详解)_helloyurenjie的博客-CSDN博客_内存四区
#include <stdio.h>
char *getMem(int num)
{
char *p1 = NULL;
p1 = (char *)malloc(sizeof(char) * num);
if (p1 == NULL)
{
return NULL;
}
return p1;
}
void main()
{
char *tmp = NULL;
tmp = getMem(10);
if (tmp == NULL)
{
return ;
}
strcpy(tmp, "111222"); //向tmp做指向的内存空间中copy数据,注意不是向指针变量tmp中
printf("tmp:%s\n", tmp);//输出tmp:111222
system("pause");
return ;
}
3.vector是怎么实现动态的?
一个报错:terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
确认是否使用vector,vector超容量时会重新申请二倍内存,因为vector会将老的一块内存,完全拷贝到另一块连续容量为2倍的vector内存中,
平时是怎么选这些数据结构的,vector,list等
熟悉哪些算法
vector的大致实现思路():我明明是写过这个题目的,却没有回答好。vector通过一个连续的数组存放元素,如果集合已满,在新增数据的时候,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,再插入新增的元素。《STL系列》之vector原理及实现 - 啊汉 - 博客园
插入新的数据可以在最后插入,也可以通过迭代器在任何位置插入。通过迭代器与第一个元素的距离知道要插入的位置,即int index=iter-begin()。这个元素后面的所有元素都向后移动一个位置,在空出来的位置上存入新增的元素。
#include <iostream>
#include <vector>
#include <algorithm>
#include <string.h>
using namespace std;
class CArray{
int size;//数组元素的个数
int *ptr;//指向动态分配的数组
public:
CArray (int s=0);
CArray(const CArray &a);
~CArray();
void push_back(int x);
int length () {return size;};
CArray& operator =(const CArray &a);
int & operator[] (int i){
return ptr[i];
}
};
CArray::CArray(int s): size(s){
if(s==0) ptr=NULL;
else{
ptr=new int[s];
}
}
CArray::CArray(const CArray &a){
/*if(a.size==0){//这一块写的有问题,感觉没有理解复制构造函数的作用
size=0;
delete []ptr;
ptr=NULL;
}*/
if(a.ptr==NULL){
ptr=NULL;
size=0;
}
else{
size=a.size;
delete[] ptr;
ptr=new int[size];
memcpy(ptr,a.ptr,sizeof(int)*a.size);
}
//return *this; 不该有这一句,复制构造函数没有返回值,也不是void
}
CArray::~CArray(){
if(ptr){
delete[] ptr;
}
}
//赋值号的作用是使得=左边对象中存在的数组,大小和内容都和右边的对象一样;要防止a=a这样的出错
CArray& CArray::operator=(const CArray& a){//可分为如果a里面的数组是空的;如果原有空间足够大,就不用分配新的空间
if(a.ptr==ptr){
return *this;
}
if(a.ptr==NULL){
if(ptr){
delete[] ptr;
}
ptr=NULL;
size=0;
return *this;
}
else{
if(a.size>size){
if(ptr){//ptr不为空
delete[] ptr;
}
ptr=new int[a.size];
}
memcpy(ptr,a.ptr,sizeof(int)*a.size);
size=a.size;
}
return *this;
}
void CArray::push_back(int x){
if(ptr){
//重新分配空间,复制原数组的内容
int* temp=new int[size+1];
memcpy(temp,ptr,sizeof(int)*size);
delete[] ptr;
ptr=temp;
ptr[size++]=x;
}
else{
ptr=new int[1];
ptr[size++]=x;
}
}
int main()
{
//String s;
//s="Good Luck,";
//cout<<s.c_str()<<endl;
//s="Shenzhou 8!";
//cout<<s.c_str()<<endl;
CArray a;
for(int i=0;i<5;++i){
a.push_back(i);
}
CArray a2,a3;
a2=a;
for(int i=0;i<a2.length();++i)
cout<<a2[i]<<" ";
cout<<endl;
a[3]=100;
CArray a4(a);
for(int i=0;i<a4.length();++i)
cout<<a4[i]<<" ";
return 0;
}
4.free为什么可以知道释放多少字节
举个例子,假设你用malloc需要申请100字节,实际上是申请了104字节。把前4字节存成该块内存的实际大小,并把前四字节之后的地址返回给你。free释放的时候会根据传入的地址向前偏移4个字节 从这四字节获取具体的内存块大小并释放。
malloc的返回类型是void *
5.野指针和内存泄露?
内存泄露:访问已经释放的内存; 访问没有权限的内存
野指针:指向内存被释放的内存或者没有访问权限的内存的指针??(p没有被及时地置为NULL,) p指向什么已经不知道了?
产生野指针的原因:1.指针没有初始化,没有初始化的指针指向什么是不确定的 2.delete或者free后没有将p置为NULL(p指向了已经释放的内存) 3.指针超越了变量的作用范围
class A
{
public:
void Func(void){ cout << “Func of class A” << endl; }
};
void Test(void)
{
A *p;
if(...)
{
A a;
p = &a; // 注意 a 的生命期
}
p->Func(); // p是“野指针”
}
————————————————
版权声明:本文为CSDN博主「qq_35212671」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35212671/article/details/51920851
延伸问题:怎么定位内存泄露? C++中有什么可以避免内存泄露
内存泄露问题只有在使用堆内存时才会出现,栈内存不存在内存泄漏问题,因为栈内存会自动分配和释放。C代码中堆内存的申请函数是malloc,常见的内存申请代码如下:
char *info = NULL; /**转换后的字符串**/
info = (char*)malloc(NB_MEM_SPD_INFO_MAX_SIZE);
if( NULL == info)
{
(void)tdm_error("malloc error!\n");
return NB_SA_ERR_HPI_OUT_OF_MEMORY;
}
由于malloc函数返回的实际上是一个内存地址,所以保存堆内存的变量一定是一个指针 。
防止内存泄露的方法:
1.内部封装:将内存的分配和释放封装到类中,在构造的时候申请内存,析构的时候释放内存。
6.宏定义和内联函数
使用函数的好处是:能够避免将相同代码重写多次的麻烦,还能减少可执行程序的体积,但也会带来程序运行时间上的开销。函数调用在执行的时候首先要在栈上面为形参和局部变量分配存储空间.......总之,使用函数调用语句和直接把函数里面的代码重新编写一遍的方式相比,节省了人力,但是带来了程序运行时间上的额外开销,一般情况下这个开销都可以忽略不计。但是如果一个函数内部本来就没有几条语句,执行时间本来就非常短,那么这个时间就不能忽略不计了。
7.智能指针
智能指针是为了解决动态内存分配时带来的内存泄露以及多次释放同一块内存空间而提出的。C++11中只能
智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
8.C++新特性
部分c++11的新特性:智能指针 shared_ptr , 无序容器(哈希表),正则表达式, Lambda表达式
无序容器:C++11新增了4种无序容器,分别是unordered_map unordered_set unordered_multimap unordered_multiset
附加内容:一些概念:
关联容器和顺序容易有着根本的不同;关联容器中的元素是按关键字来保存和访问的。与之相对,顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。
模板,现在还没有看
9.构造函数和析构函数
面向对象的语言倾向于对象一定要初始化后,使用起来才比较安全。构造函数就是一类特殊的成员函数,其名字和类的名字一样,不写返回值类型,可以重载。
析构函数:一个类有且仅有一个析构函数。如果定义类时没有编写析构函数,则编译器生成默认析构函数。析构函数在对象消亡时自动被调用
10.多态
这里需要先看一下14.8节的内容:基类与派生类的指针的互相转换
多态:多态就是不同继承类的对象,对同一消息做出不同的响应,基类的指针指向或绑定到派生类的对象,使得基类指针呈现不同的表现方式。
我的回忆:多态是通过虚函数表实现的,每一个含有虚函数的类以及含有虚函数的类的派生类的每一个对象都有多出的4字节来保存基类/派生类的虚函数表的地址。
11.孤儿进程和僵尸进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将称为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对他们完成状态手机工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait 或waitpid
获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程
12.查看占用了某个端口的运行(执行)的完整路径的方法
netstat 找出运行在指定端口的进程
#netstat -an | grep '80"
#netstat -l 只显示监听端口
通过ps及top命令查看进程信息时,只能查到相对路径,查不到进程的详细信息,如绝对路径
top所显示的信息有:进程id
USER-进程所有者
PR-进程
NI
VIRT
RES
SHR
%CPU--上次更新到现在的CPU时间占用百分比
%MEM ----进程使用的物理内存百分比
TIME+ — 进程使用的CPU时间总计,单位1/100秒
COMMAND — 进程名称(命令名/命令行)
Linux在启动一个进程时,系统会在/proc下创建一个以PID命名的文件夹,在该文件夹下会有我们的进程信息,其中包括一个名为exe的文件即记录了绝对路径?
linux下进程运行后,进程信息存储在/proc/进程id 目录下面,进程id查看命令 ps -ef | grep 进程名,vi /proc/进程id/environ 搜索PWD字段,则是该进程运行所在目录。