C++面试题(1)

文章讲述了面试中遇到的基础编程问题,包括new和delete、malloc和free的区别,二叉树遍历,递归注意事项,lambda表达式以及避免头文件重复包含的方法。作者反思了自己的面试经历,提醒读者注意这些易错点。
摘要由CSDN通过智能技术生成

实在不想说自己的面试过程有多惨烈了,现在自己一个人想想都是尬到抠出几室几厅的程度,基础实在是太差了,不得不承认自己就是一个API程序员……
但还是希望自己能够成长,内心强大一些,大不了再重新开始。
废话不多说了,以下是自己面试的实况转播,希望看到的人如果和我一样,不要再范同样低级的错误……

1. new、delete和malloc、free的区别

①类型不同

new、delete是c++的运算符,可以进行重载,而malloc和free是c的标准库函数。

②返回类型不同

new 直接带具体的类类型,malloc则是返回void*类型,需要进行类型转换。

③执行的操作不同

new一般分为两步:new操作和构造。new操作对应与malloc,但new操作可以重载,可以自定义内存分配策略,不做内存分配,甚至分配到非内存设备上,而malloc不行。new 可以执行构造函数,delete会执行析构函数,而malloc、free不会。

该问题是个比较常见的问题了,本以为我会答的很好,但是让在现场手写,我的大脑就空白了。(这是面试的第一个问题也是全场最让我尴尬的时刻,虽然当时并未觉得,但这一秒的我回想一天前的自己,也只能感慨真是少妇英勇,无知无畏啊~~~~~~~~)

    /* ----- 基础类型数组的创建----------- */
    // ①new/delete
    int *a = new int[10];
    for (int i = 0; i < 10; ++i)
        a[i] = i;
    delete[] a;
    a = nullptr;
    
    // ②malloc和free
    int *b = (int*)malloc(sizeof(int) * 10);
    for (int i = 0; i < 10; ++i)
        b[i] = i;
    free(b);
    b = nullptr;

    /* ----- 自定义类型数组的创建----------- */
    D* pArr = new D[10];
    delete[] pArr;
    pArr = nullptr;

    D* pArr2 = (D*)malloc(sizeof(D) * 10);
    free(pArr2);
    pArr2 = nullptr;

2.二叉树的深度和层序遍历

void dfs(TreeNode* node)
{
    if (node == nullptr)
        return;
    // 访问根节点的值
    dfs(node->left);
    dfs(node->right);
}

void bfs(TreeNode* node)
{
    if (node == null)
        return;
    queue<TreeNode*> q;
    q.push(node);
    while(!q.empty())
    {
        int size = q.size();
        for (int i = 0; i < size(); ++i)
        {
            TreeNode* temp = q.front();
            q.pop();
            // 处理值
            if (temp->left)
                q->push(temp->left);
            if (temp->right)
                q->push(temp->right);
         }
    }
}

3 递归需要注意什么

① 终止条件:必须要有终止条件,否则将无休止的递归下去,直至资源耗尽。
②栈的开销:每调用一次函数都有栈的开销,防止出现深度过深,内存消耗太大的情况出现。(这一点当时没有说,忘记想到内存这方面了。)

4 lambda表达式

lambda表达式可以理解为一个匿名的内联函数。
语法:[capture list] (parameter list) -> return type {function body}

capture list:表示捕获列表,是一个lambda所在函数中定义的局部变量列表
parameter list:表示参数列表
return type:返回类型
function body:函数体

可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体,忽略参数列表等价于指定一个空函数列表,忽略返回类型,lambda会根据函数体中的代码推断出来(如果函数体直接return,则是void类型)。例如:

auto f = [ ] {return 42;};
cout << f() << endl;

捕获列表

类型说明
[]空捕获列表。lambda不能使用所在函数中的变量。一个lambda只有在捕获变量后才能使用它们
[ names ]names 是使用逗号分隔的名字裂变,这些名字都是lambda所在函数的局部变量。默认情况下是值捕获,名字前加&指明是引用捕获。
[ = ]采用值捕获方式,lambda体将拷贝所使用的来自所在函数的实体的值。就是能够捕获所有父作用域中的变量(包括this)
[ & ]采用引用方式捕获,lambda体中所使用的来自所在函数的实体的值。就是表示引用传递捕捉所有父作用域中的变量(包括this)。
// [] 空捕获列表
void test()
{
    int num = 9;
    auto f = [] { return num;  }; // 提示语法错误,num不能被捕获
}

void test1()
{
    int num = 9;
    auto f = [num] { return num; };
}

参考的原文链接

5.怎样避免头文件重复包含

① 宏定义的方式

#ifndef _NAME_H
#define _NAME_H//头文件内容#endif

② 使用#pragma once避免重复引入

将其附加到指定文件的最开头位置,则该文件就只会被 #include 一次。

#ifndef 是通过定义独一无二的宏来避免重复引入的,这意味着每次引入头文件都要进行识别,所以效率不高。但考虑到 C 和 C++ 都支持宏定义,所以项目中使用 #ifndef 规避可能出现的“头文件重复引入”问题,不会影响项目的可移植性。

和 ifndef 相比,#pragma once 不涉及宏定义,当编译器遇到它时就会立刻知道当前文件只引入一次,所以效率很高
③ 使用_Pragma操作符

当处理头文件重复引入问题时,可以将_Pragma("once")如下语句添加到相应文件的开头。

其可以看做是 #pragma 的增强版,不仅可以实现 #pragma 所有的功能,更重要的是,_Pragma 还能和宏搭配使用。

6 拷贝构造函数的参数为什么是引用

这道题第一反应是减少一次拷贝,但是对于拷贝构造函数,仅仅是减少拷贝这个答案是不对的。(可惜当时我知道是不对,但是却没有回答出其他的答案,唉!!!!)
原因如下:如果拷贝构造函数中的参数如果不是一个引用,那么就相当于采用了传值的方式,而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。因此拷贝构造函数必须是一个引用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值