【C++】面试题整理(未完待续)
【C++】面试题整理
文章目录
一、概述
最近面试,感觉有些比较基础的好久不用就有些生疏了,整理一下。
【备注】:题目的答案是个人整理的不能保证是标准答案。
二、C++基础
2.1 - 指针在 32 位和 64 位系统中的长度
4 字节和 8 字节
32/8 = 4
64/8 = 8
2.2 - 数组和指针
以下代码在 32 位和 64 位系统中分别打印什么?
char a[] = "123456789";
const char* b = "123456789";
std::cout << sizeof(a) << std::endl;
std::cout << sizeof(b) << std::endl;
- 32位打印: 10 和 4
- 64位打印: 10 和 8
指针已经在第一题中说过了,就不重复了。char 数组的大小需要加上最后的 \0
。
2.3 - 结构体对齐补齐
在 32 位和 64 位系统中分别是多少
struct M
{
int a;
double b;
};
printf("%d", sizeof(struct M));
答案: 均为16,这里是结构体的对齐补齐,与系统位数无关。
2.4 - 头文件包含
两种包含头文件的区别
#include <stdio.h>
#include "stdio.h"
- 尖括号,编译器会从系统目录中查找;
- 双引号,编译器会首先从当前项目目录查找,找不到再去系统目录中查找。
虽然#include"“的查找范围更广,但是这并不意味着,不论是系统头文件,还是自定义头文件,一律用#include”“包含。因为#include”"的查找顺序存在先后关系,如果项目当前目录或者引用目录下存在和系统目录下重名的头文件,那么编译器在当前目录或者引用目录查找成功后,将不会继续查找,所以存在头文件覆盖的问题。另外,对于系统头文件,用#include<>包含,查找时一步到位,程序编译时的效率也会相对更高。
2.5 - 堆和栈的区别
- 堆(heap): 向上增长内存,存储动态分配的内存,需要程序员自己管理,分配和释放。
- 栈(stack): 向下增长内存,存储静态分配的内存,系统会自动释放。
2.6 - 宏函数比较两个数值的大小
要求不使用大于、小于和if
#define MAX(a,b) (int)((a)/(b)) == 0 ? (a) : (b);
定义宏函数需要注意,入参需要使用小括号括起来,避免传入算式导致预期外的计算
MAX(4-1,5);
// 不加小括号 4 - 1/5 = 3 返回3 值错误。
// 加小括号 (4-1)/(5) = 0, 返回值为 5。
2.7 - 冒泡排序
void bubbleSort(int arr*, int n)
{
for (int i = 0; i < n - 1; ++i)
{
for (int j = 0; j < n - i - 1; ++j)
{
if (arr[j] > arr[j+1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = arr[j];
}
}
}
}
如果我是面试官,我会在这里考一个打印 arr 的 sizeof ,因为 数组在传参时会退化为指针。
2.8 - 菱形继承的内存布局
class A
{
public:
int a;
};
class B : public A
{
public:
int b;
};
class C : public A
{
public:
int c;
};
class D : public B, public C
{
public:
int d;
};
可以看到 A 中的内容被重复记录了两次
若改为虚继承,虚继承是为了解决冗余和二义性
class B : virtual public A {/*...*/};
class C : virtual public A {/*...*/};
则会出现一个虚基表指针 vbptr (virtual base pointer) 记录偏移
VS Developer Command Prompt 查看内存布局方法
cl /d1 reportSingleClassLayoutD filename.cpp
2.9 - 继承重写
实现一个 Shape 类,定义纯虚函数 area,定义 Rectangle 和 Circle 类继承 Shape 类,在main函数中打印面积。
class Shape
{
public:
virtual double area() = 0;
};
class Rectangle : public Shape
{
public:
Rectangle(double width, double length):
m_width(width), m_length(length) {}
virtual double area() override
{
return m_width*m_length;
}
private:
double m_width {0.0};
double m_length {0.0};
};
#define PI 3.1415926
class Circle : public Shape
{
public:
Circle(double radius): m_radius(radius)
{}
virtual double area() override
{
return PI * m_radius * m_radius
}
private:
double m_radius{0.0};
};
调用
#include <iostream>
int main(int argc, char* argv[])
{
Rectangle rect(3.0, 4.0);
Circle c(2.0);
std::cout << rect.area() << std::endl;
std::cout << c.area() << std::endl;
}
2.10 - 如何禁止类在栈上分配内存
其实只要禁用 析构就可以在编译时报错,但是为了方便继承和使用
class A
{
public:
static A* Create(){ return new A();}
void Destroy() { delete this;} // 不能为 static,因为静态函数中无 this 指针
protected:
~A() {};
A() {};
A(const A& rhs) {/*...*/};
A(const A&& rhs) {/*...*/};
const A& operator=(const A& rhs) {/*...*/};
const A& operator=(const A&& rhs) {/*...*/};
};
三、智能指针
3.1 - 智能指针是线程安全的吗?
不是,虽然引用计数是原子操作,但还有其他的操作
3.2 - 线程安全的几种方法
- 加锁
- 原子变量
std::atomic
四、 STL
4.1 - map 删除
改错。
using std::map;
using std::string;
int main(int argc, char* argv[])
{
map<string, string> mapData;
mapData["a"] = "aaa";
mapData["b"] = "bbb";
mapData["c"] = "ccc";
for (map<string, string>::iterator i=mapData.begin(); i!=mapData.end(); i++)
{
if (i->first == "b")
{
mapData.erase(i);
}
}
return 0;
}
错误是容器删除元素会有迭代器失效,网上给的改错方式是,将for循环 i++
删除 加到函数中
for (map<string, string>::iterator i=mapData.begin(); i!=mapData.end(); /*i++*/)
{
if (i->first == "b")
{
mapData.erase(i++);
}
else
{
i++;
}
}
个人感觉这样也可以
for (map<string, string>::iterator i=mapData.begin(); i!=mapData.end();++i)
{
if (i->first == "b")
{
i = mapData.erase(i);
--i;
}
}
五、参考
- https://blog.csdn.net/rammuschow/article/details/107947302 include
- https://en.cppreference.com/w/cpp/container/map/erase std::map erase 函数
- https://blog.csdn.net/AgoniAngel/article/details/105893798 菱形继承内存分布
原文地址:https://blog.csdn.net/weixin_44488341/article/details/145160837
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!