面试真题 | 美团校招面经

@[toc]

一面

TCP和UDP的区别?

以下是TCP和UDP之间几个关键的区别:

  1. 连接与无连接

    • TCP(传输控制协议)是面向连接的协议。在数据交换之前,TCP需要在通信双方之间建立一个连接。这个连接过程包括三次握手,确保双方都已准备好接收数据。
    • UDP(用户数据报协议)是无连接的协议。UDP发送数据之前不需要建立连接,因此它是面向事务的简单不可靠信息传送服务。
  2. 可靠性

    • TCP提供了可靠的数据传输服务。它通过序列号、确认应答、超时重传、流量控制、拥塞控制等机制确保数据的完整性和顺序性。
    • UDP则不保证数据传输的可靠性。如果因为网络问题导致数据丢失或损坏,UDP不会进行任何恢复操作,它仅仅是将数据报尽最大努力交付给接收方。
  3. 头部开销

    • TCP的头部较大(通常20字节,可选字段会增加其大小),包含了序列号、确认号、窗口大小等控制信息,这些信息用于确保数据的可靠传输。
    • UDP的头部较小(只有8字节),只包含必要的端口号、长度和校验和等信息,因此UDP的传输效率更高,但牺牲了可靠性。
  4. 应用场景

    • TCP适用于需要可靠传输的应用场景,如文件传输(FTP)、网页浏览(HTTP)、电子邮件(SMTP)等。
    • UDP适用于对实时性要求较高,但可以容忍少量数据丢失的应用场景,如视频流、实时音频传输(VoIP)、DNS查询等。
  5. 流量控制和拥塞控制

    • TCP通过滑动窗口协议进行流量控制,防止发送方发送数据过快导致接收方处理不过来。同时,TCP还通过慢启动、拥塞避免、快重传、快恢复等算法进行拥塞控制,避免网络拥塞。
    • UDP不提供流量控制和拥塞控制机制,它假设网络状况良好,数据传输的可靠性由应用层来保障。

I/O复用解决什么问题?为什么使用I/O复用?

这个问题考的是对网络编程中资源管理、效率提升以及并发处理的理解。

I/O复用解决什么问题?

I/O复用主要解决的是在高并发网络编程中,单个进程或线程同时处理多个网络连接时的效率问题。在传统的网络编程模型中,如果服务器需要同时监听多个socket,它可能会为每个socket创建一个新的进程或线程。然而,这种方法在连接数增多时会带来显著的资源消耗(如内存和CPU),导致系统性能下降,甚至崩溃。

I/O复用技术允许单个进程或线程同时监听多个文件描述符(socket),并能够在这些文件描述符中的任何一个准备好I/O操作(如读、写)时得到通知。这样,进程或线程就可以非阻塞地等待多个I/O操作,从而提高了资源利用率和程序的吞吐量。

为什么使用I/O复用?

  1. 提高资源利用率:通过复用单个进程或线程来处理多个I/O操作,减少了系统资源的消耗(如创建和销毁进程/线程的开销)。

  2. 提升系统吞吐量:由于减少了因等待I/O操作而阻塞的进程/线程数量,系统能够同时处理更多的网络连接,从而提高了整体的吞吐量。

  3. 简化编程模型:I/O复用使得开发者能够编写出更简洁、更易于维护的代码,因为不再需要为每个连接单独创建一个进程或线程。

  4. 更好的可伸缩性:随着网络连接的增加,传统的多进程/多线程模型可能会遇到资源瓶颈。而I/O复用模型则能够更好地适应高并发场景,具有更好的可伸缩性。

  5. 支持异步I/O:虽然I/O复用本身并不直接等同于异步I/O,但它为实现异步I/O提供了基础。通过使用非阻塞I/O和事件通知机制,开发者可以构建出高效的异步I/O系统。

常见的I/O复用技术

  • select:是最早的I/O复用技术之一,但它有一些限制,比如单个进程能够监视的文件描述符的数量有限(通常在1024个左右),并且当文件描述符数量很大时,效率较低。

  • poll:是select的改进版本,它不受文件描述符数量的限制,但在处理大量文件描述符时效率仍然不高。

  • epoll(Linux特有):是select和poll的进一步增强,它提供了更高的效率和更好的可伸缩性。epoll使用基于事件的通知机制,当文件描述符就绪时,它会通知进程进行处理,而不是让进程不断地轮询检查。这使得epoll在处理大量并发连接时具有非常出色的性能。

问项目中CPP语法问题?

项目中算法?

项目问题。

  • 你写在简历上的项目一定要事无巨细,搞清楚哦。

二面 一周后 (1h)

进行自我介绍

详细讲下对你提高很大的项目?

有逻辑,金字塔原理。

哪个项目?

为什么?

是因为让你学到了什么?

升华一下!

写一下类的拷贝构造

在C++中,拷贝构造函数是一个特殊的构造函数,它用于创建一个新对象作为另一个同类型对象的副本。拷贝构造函数在以下几种情况下被自动调用:

  1. 当通过值传递对象给函数时。
  2. 当从一个对象初始化另一个同类型的对象时(使用等号=)。
  3. 当函数返回对象时(包括临时对象)。

拷贝构造函数的声明通常如下:

class ClassName {
public:
    // 默认构造函数
    ClassName() {
        // 初始化代码
    }

    // 拷贝构造函数
    ClassName(const ClassName& other) {
        // 拷贝other到当前对象
    }

    // 其他成员函数...
};

这里是一个简单的例子,演示了如何为一个包含动态分配内存的类编写拷贝构造函数:

#include <iostream>
using namespace std;

class String {
private:
    char* data;

public:
    // 默认构造函数
    String(const char* str = "") {
        if (str) {
            data = new char[strlen(str) + 1];
            strcpy(data, str);
        } else {
            data = new char[1];
            *data = '\0';
        }
    }

    // 拷贝构造函数
    String(const String& other) {
        data = new char[strlen(other.data) + 1];
        strcpy(data, other.data);
    }

    // 析构函数
    ~String() {
        delete[] data;
    }

    // 输出字符串
    void print() const {
        cout << data << endl;
    }

    // 其他成员函数...
};

int main() {
    String s1("Hello");
    String s2 = s1; // 调用拷贝构造函数

    s1.print(); // 输出: Hello
    s2.print(); // 输出: Hello

    return 0;
}

在这个例子中,String类包含了一个指向字符数组的指针data,用于存储字符串。拷贝构造函数String(const String& other)接收一个对同类型对象的常量引用作为参数,并使用strlenstrcpy函数来复制other对象的字符串到当前对象的data指针所指向的内存中。注意,这里使用了new来分配内存,因此必须在析构函数中使用delete[]来释放内存,以避免内存泄漏。

记住,如果你没有为你的类提供拷贝构造函数,编译器会为你生成一个默认的拷贝构造函数,该构造函数会进行浅拷贝(shallow copy)。然而,在包含动态分配内存或资源管理(如文件句柄、网络连接等)的类中,浅拷贝通常是不安全的,因为它会导致多个对象共享相同的资源,并在析构时多次释放这些资源,从而引发错误。因此,在这些情况下,你需要自己编写一个拷贝构造函数来执行深拷贝(deep copy)。

虚函数的构造,能否有delete函数,构造函数能否有虚函数?

在C++中,关于虚函数、构造函数和delete函数(这里我假设你指的是析构函数,因为C++标准中并没有直接称为delete的函数,但析构函数在对象销毁时自动调用,其效果类似于"删除"对象)有一些重要的规则和限制。

虚析构函数

可以有虚析构函数。虚析构函数主要用于在通过基类指针删除派生类对象时,确保能够正确地调用派生类的析构函数,以避免资源泄漏或未定义行为。当基类指针指向派生类对象,且你希望通过该基类指针删除该对象时,必须在基类中声明虚析构函数。

class Base {
public:
    virtual ~Base() {} // 虚析构函数
};

class Derived : public Base {
    // ...
};

Base* ptr = new Derived();
delete ptr; // 正确调用Derived的析构函数

构造函数不能是虚函数

构造函数不能是虚函数。这是因为构造函数是在对象创建时调用的,而虚函数的机制依赖于对象的vptr(虚函数表指针),该指针在对象构造期间还未被初始化。因此,在构造函数调用时,编译器无法确定应该调用哪个类的构造函数。此外,构造函数的目的就是初始化对象,此时对象的具体类型(基类还是派生类)是已知的,因此不需要虚函数机制来确定调用哪个构造函数。

是否有“dele

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

ARM/Linux嵌入式真题 文章被收录于专栏

让实战与真题助你offer满天飞!!! 每周更新!!! 励志做最全ARM/Linux嵌入式面试必考必会的题库。 励志讲清每一个知识点,找到每个问题最好的答案。 让你学懂,掌握,融会贯通。 因为技术知识工作中也会用到,所以踏实学习哦!!!

全部评论

相关推荐

无情咸鱼王的秋招日记之薛定谔的Offer:好拒信,偷了,希望有机会用到
点赞 评论 收藏
分享
Noel_:中石油是这样的 哥们侥幸混进免笔试名单 一看给我吓尿了
点赞 评论 收藏
分享
点赞 7 评论
分享
牛客网
牛客企业服务