C++面试

1. inline 失效场景

在以下情况下,使用 inline 修饰符可能会失效:

  1. 过度使用 inline 会让程序的可读性和可维护性降低,因为 inline 函数的定义通常是放在头文件中的,当头文件包含了大量的 inline 函数时会增加编译时间和源代码文件的大小,同时也会增加代码库的依赖关系。

  2. 在某些情况下,编译器可能会选择不将函数内联,例如函数体过于庞大,或者函数包含循环或递归等结构。这时候使用 inline 修饰符甚至可能会降低程序性能,因为函数调用和返回可能会比函数内部执行代码的开销更大。

  3. 对于虚函数、递归函数和函数指针等,inline 修饰符是不起作用的。

因此,使用 inline 修饰符需要谨慎评估其对程序性能和可维护性的影响,并在需要时进行适当的优化。

2. event_loop 了解吗

当我们涉及到事件驱动编程时,常常会使用一个“事件循环”(event loop),它可以管理各种 I/O 操作,以及其他事件。在 C++ 中实现这个事件循环需要依靠不同的库,例如 Boost.Asio。下面是一段使用 Boost.Asio 的 C++ 代码示例:

#include 

int main() {
  // 创建一个 I/O 上下文,它会被所有的 I/O 操作共享
  boost::asio::io_context io_context;

  // 创建一个 TCP 套接字
  boost::asio::ip::tcp::socket socket(io_context);

  // 连接到一个远程主机
  boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::make_address("127.0.0.1"), 1234);
  socket.async_connect(endpoint, [](const boost::system::error_code& error) {
    if (!error) {
      std::cout << "Connected!\n";
    } else {
      std::cout << "Failed to connect: " << error.message() << "\n";
    }
  });

  // 开始事件循环
  io_context.run();
  
  return 0;
}

这段代码创建了一个 I/O 上下文,然后创建了一个 TCP 套接字,并使用异步连接操作连接到另一台计算机上。最后,通过 io_context.run() 方法启动事件循环,程序将在此处阻塞,直到所有的 I/O 操作完成。在此期间,事件循环会不断地检查 I/O 事件、计时器事件和其他事件,并调用相应的回调函数处理这些事件。

3.Qt信号和槽机制的优缺点

Qt的信号和槽机制是一种非常方便的事件处理机制,它允许不同对象之间的通信,同时避免了很多手动事件连接和处理的烦恼。以下是信号和槽机制的几个优缺点:

优点:

  1. 松耦合性:信号和槽机制实现了对象之间的松耦合,只需要发送信号,而不需要知道谁接收信号或者信号具体被如何处理。这种松耦合性允许修改一个对象而不影响到其他对象。

  2. 易于理解和使用:在Qt中使用信号和槽机制非常简单,可以很方便的定义自己的信号和槽,也可以连接不同对象之间的信号和槽。这种机制非常直观且易于使用。

  3. 高效性:由于信号和槽机制是Qt框架中自带的机制,因此它的实现非常高效。底层实现非常优秀,运行速度非常快。

缺点:

  1. 信号和槽机制的底层实现会占用一定的内存和时间,如果信号和槽连接过多,会对应用程序效率产生一定影响。

  2. 信号和槽机制的调试相对比较困难,由于信号和槽机制不是传统的函数调用,因此在调试时需要一定的经验和技巧。

总的来说,信号和槽机制的优点要大于缺点,这也是为什么Qt框架中广泛使用信号和槽机制的原因。

4.信号和槽怎么实现同步/异步控制?

信号和槽是Qt框架中的一个重要机制,可以实现对象之间的通信。同时也可以实现同步/异步控制。

对于同步任务来说,直接使用信号和槽即可。例如,当按钮被点击时,会发出一个信号。通过连接这个信号到槽函数中,可以实现按钮被点击时会执行槽函数。

对于异步任务来说,使用Qt提供的QThread类来创建一个新线程,把耗时的任务放在这个新线程中执行。可以通过connect函数将QThread对象的信号与槽函数连接起来,实现异步任务的控制。

例如,在子线程中对一个文件进行读写操作,在完成操作后,子线程发出一个信号,主线程中的槽函数接收到信号,就可以在主线程中对读写结果进行处理。

总之,使用信号和槽可以实现同步/异步控制,具体实现可以依据具体场景进行选择。同时,需要注意信号和槽的连接都是线程安全的。

5.socket通信过程?

Socket通信是一种应用层协议,主要用于不同计算机或进程间的通信。它用于通过网络在不同设备之间传输数据,可以基于面向连接或面向无连接的传输协议实现。

通常情况下,Socket通信过程包括以下步骤:

  1. 创建Socket套接字:首先需要创建一个Socket套接字,用于通信。在创建Socket时,可以指定Socket的类型、协议和端口号等参数。

  2. 绑定Socket到本地地址和端口号:Socket一般会绑定到本地地址和端口号,以便对外提供服务。在绑定时,必须指定本地地址和端口号,使得应用程序能够通过特定的地址和端口号进行通信。

  3. 监听客户端连接:在服务器端需要监听客户端的连接请求。当客户端请求连接时,服务器端会创建一个新的Socket套接字与客户端进行通信。

  4. 接受客户端连接:当客户端请求连接时,服务器端会调用accept函数接受客户端连接。accept函数返回一个新的Socket套接字用于和客户端通信。

  5. 进行通信:通过Socket套接字进行通信。在通信时,使用send和recv函数进行数据的发送和接收。

  6. 关闭连接:通信完成后,需要关闭Socket套接字,释放资源。

需要注意的是,不同的协议和操作系统可能会有一些不同的细节实现。此外,Socket通信常用的协议包括TCP/IP和UDP等。

6.线程的同步方法

线程的同步方法有多种,下面列举几种常用的方法:

1. 互斥锁:通过锁住共享资源来保证多个线程访问该资源时的同步性。在另一个线程需要访问共享资源时,需要等待该互斥锁被释放后才能访问。

2. 条件变量:是一种可以实现线程通信的同步机制,通过等待、唤醒等操作使线程能够有序地访问共享资源。

3. 信号量:也是一种实现线程同步的机制,不同的是它通过计数器来控制线程对资源的访问,使得多个线程能够协调共同访问该资源。

4. 原子操作:是一种较为底层的同步机制,其基本操作是可以原子执行的,避免了多线程访问共享资源时的冲突。常见的原子操作有原子加载、原子存储、原子递增、原子递减等。

以上的方法既可以单独使用,也可以组合使用,根据实际情况选择适合的同步机制进行线程同步。

7.讲一下vector和链表的区别

vector和链表都是用于存储数据的容器,但它们的内部实现机制有很大的不同。

vector是基于数组的可变大小容器,它的元素在内存中是连续存储的,这样就使得访问元素时的时间复杂度为O(1),并且可以通过下标随机访问任意元素。但是受制于数组的内存结构,其插入和删除的操作可能会导致大量元素的移动,时间复杂度为O(n),而且在容器中间插入或删除元素时,其后续元素的下标都需要重新计算。

链表则采用数据结构中的链式存储,它的每个节点都是通过指针来互相链接起来的,这使得插入和删除的操作的时间复杂度只为O(1),而没有元素移动的问题。但因为它的元素在内存中不是连续存储的,在访问元素时需要从头开始遍历链表,时间复杂度为O(n),这也使得它不支持下标随机访问。

在选择容器的使用时,根据具体情况进行取舍。如果经常需要在容器的中间插入删除元素,或者对容器进行动态大小调整时,链表是一个不错的选择。而在需要随机访问元素时,或者对容器的内存使用有较高要求时,vector更为适合。

你可能感兴趣的:(面经,面试,职场和发展,c++,链表,数据结构)