一、C++11的新特性都有哪些?
1.1 自动类型推断 (auto)
auto
关键字允许编译器自动推断变量的类型,从而简化代码的书写。
auto num = 5; // int
auto pi = 3.14; // double
auto str = "Hello"; // const char*
1.2 范围 for 循环
范围 for 循环可以简化容器(如数组和 STL 容器)的遍历。
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto& i : vec) {
std::cout << i << " "; // 输出 1 2 3 4 5
}
1.3 Lambda 表达式和函数闭包
Lambda 表达式提供了一种简洁的方式来定义匿名函数,并允许捕获外部变量。
int x = 10;
auto lambda = [x](int y) { return x + y; };
std::cout << lambda(5); // 输出 15
1.4 右值引用和移动语义
右值引用允许对临时对象进行高效的资源管理,移动语义使得资源的转移比复制更高效。
#include <utility>
class MyClass {
public:
MyClass() { /* 资源分配 */ }
MyClass(MyClass&& other) { /* 资源转移 */ }
};
MyClass createMyClass() {
return MyClass(); // 返回一个右值
}
MyClass obj = createMyClass(); // 通过移动构造
1.5 可变参数模板
可变参数模板允许函数接受任意数量的参数。
template<typename... Args>
void printAll(Args... args) {
(std::cout << ... << args) << std::endl; // C++17 折叠表达式
}
printAll(1, 2, 3.5, "Hello"); // 输出: 1 2 3.5 Hello
1.6 初始化列表
初始化列表提供了一种更方便的方式来初始化容器和自定义类型。
std::vector<int> vec = {1, 2, 3, 4}; // 列表初始化
std::array<int, 3> arr = {1, 2, 3}; // std::array 初始化
1.7 强类型枚举
强类型枚举(enum class
)提供了更强的类型安全性和作用域。
enum class Color { Red, Green, Blue };
Color c = Color::Red;
// c = 1; // 错误,不能隐式转换
1.8 智能指针如 std::unique_ptr
和 std::shared_ptr
智能指针帮助管理动态分配的内存,避免内存泄漏。
#include <memory>
std::unique_ptr<int> p1 = std::make_unique<int>(5); // 独占型智能指针
std::shared_ptr<int> p2 = std::make_shared<int>(10); // 共享型智能指针
1.9 空指针关键字 (nullptr)
nullptr
是一个类型安全的空指针常量,取代了 NULL
。
int* p = nullptr; // p 是一个空指针
1.10 线程库支持
C++11 引入了 <thread>
头文件,使得多线程编程变得更加简单。
#include <thread>
void threadFunction() {
std::cout << "Hello from thread!" << std::endl;
}
std::thread t(threadFunction);
t.join(); // 等待线程结束
1.11 新容器如 std::array
和 std::unordered_map
C++11 引入了一些新的标准容器。
std::array
是一个固定大小的数组,可以在编译时确定大小。std::unordered_map
是一个基于哈希表的关联容器,提供常量时间复杂度的插入和查找。
#include <array>
#include <unordered_map>
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::unordered_map<std::string, int> umap = {{"one", 1}, {"two", 2}};
这些特性极大地增强了 C++ 的功能和可用性,使得编写现代 C++ 代码更加简洁、易于维护和高效。
二、了解什么设计模式?单例了解吗?
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实
例。
在C++中实现单例模式通常需要以下步骤:
私有化构造函数和析构函数:这样可以防止外部代码直接创建或销毁该类的实例。
提供一个私有的静态指针变量:该指针用于存储唯一实例的地址。
提供一个公共的静态方法:这个方法用于返回唯一实例,并确保实例在第一次调用时被创建。
#include <iostream>
#include <mutex>
class Singleton {
private:
// 私有构造函数
Singleton() {
std::cout << "Singleton instance created!" << std::endl;
}
// 私有析构函数
~Singleton() {
std::cout << "Singleton instance destroyed!" << std::endl;
}
// 私有静态指针,指向唯一实例
static Singleton* instance;
static std::mutex mutex_; // 线程安全的互斥锁
public:
// 禁止拷贝构造和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 公共静态方法获取唯一实例
static Singleton* getInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mutex_); // 确保线程安全
if (instance == nullptr) {
instance = new Singleton();
}
}
return instance;
}
};
// 初始化静态指针
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex_;
int main() {
Singleton* singleton1 = Singleton::getInstance();
Singleton* singleton2 = Singleton::getInstance();
// 验证两者是同一个实例
if (singleton1 == singleton2) {
std::cout << "Both are the same instance." << std::endl;
}
return 0;
}
三、TCP和UDP的区别?
TCP是面向连接的协议,进行数据传输前需要建立连接。
UDP是无连接的协议,不需要建立连接就可以直接发送数据。
3.2.可靠性:
TCP提供可靠的数据传输,确保数据完整性和顺序。
UDP提供不可靠的数据传输,可能出现丢包,不保证数据顺序。
3.速度:
·TCP相对较慢,因为它需要确认机制和错误
校正。
·UDP传输速度更快,没有确认机制,适用于
对速度要求高的场合。
4.数据流:
·TCP提供字节流服务,通过数据流的方式发
送数据。
·UDP以数据报文的形式发送信息,发送独立
的消息。
3.1 连接
-
TCP(传输控制协议):
- TCP 是面向连接的协议,这意味着在实际数据传输之前,必须先建立一个连接。这是通过三次握手(Three-Way Handshake)完成的,确保双方都准备好进行通信。
- 三次握手的过程如下:
- 客户端发送一个 SYN(同步)包到服务器。
- 服务器回应一个 SYN-ACK(同步-确认)包。
- 客户端再发送一个 ACK(确认)包来确认连接建立。
-
UDP(用户数据报协议):
- UDP 是无连接的协议,发送方和接收方之间不需要建立连接。数据可以立即发送,适合需要快速传输且对连接不敏感的应用。
3.2 可靠性
-
TCP:
- TCP 提供可靠的数据传输,确保数据的完整性和按顺序到达。它使用序列号来跟踪每个字节的发送和接收,采用重传机制来处理丢失的数据包,并通过校验和来检测数据错误。
-
UDP:
- UDP 不提供可靠性保证,数据包可能会丢失、重复或乱序到达。由于没有重传机制和流控制,UDP 适用于对实时性要求高的应用,比如视频会议、在线游戏等。
3.3 速度
-
TCP:
- 因为 TCP 需要进行连接建立、数据确认、错误检测和重传等操作,所以相对较慢。它适合需要高可靠性的数据传输,像文件传输、电子邮件等。
-
UDP:
- UDP 传输速度较快,因为它没有连接建立过程和确认机制,适用于速度要求高且可以容忍数据丢失的场景,例如 DNS 查询、实时视频和音频传输。
3.4 数据流
-
TCP:
- TCP 是面向字节流的协议,它将数据视为一个连续的字节流。应用程序发送的数据会被分割成多个数据包,接收方再将这些数据包重新组装成完整的数据流。
-
UDP:
- UDP 是面向消息的协议,每个数据包(称为数据报)都是独立的。数据报的大小有限,UDP 不会将多个数据报组合成一个流,需要应用程序自己处理消息的边界。
四、左值和右值引用?
左值引用是对可寻址的、可重复使用的对象(左值)的引用。它使用传统的单个&符号。
右值引用是对临时对象(右值)的引用,使用双&&符号。右值引用允许实现移动语义和完美转发,它可以将资源从一个(临时的)对象转移到另一个对象,提高效率,避免不必要的复制。
五、能说说static关键字吗?
5.1 static关键字有几个用途:
1. 当在类的数据成员前使用 static
时,表示该成员属于类本身,而不是任何特定的实例。所有实例共享这一成员。
class MyClass {
public:
static int count; // 静态数据成员
MyClass() {
count++; // 每创建一个实例,count 增加
}
};
// 静态成员定义和初始化
int MyClass::count = 0;
int main() {
MyClass obj1;
MyClass obj2;
std::cout << MyClass::count; // 输出 2,表明创建了两个实例
return 0;
}
2.当在类的成员函数前使用 static
时,可以在不创建类实例的情况下直接调用该函数。这通常用于那些不依赖于类的实例的功能。
class MyClass {
public:
static void display() {
std::cout << "Hello from static function!" << std::endl;
}
};
int main() {
MyClass::display(); // 直接调用静态成员函数
return 0;
}
3.在函数内部使用static定义局部变量,使得该变量的值在函数调用间持久存在,而不是每次调
用时都重新创建。
#include <iostream>
void counter() {
static int count = 0; // 静态局部变量
count++;
std::cout << "Count: " << count << std::endl; // 每次调用显示当前计数
}
int main() {
counter(); // 输出 Count: 1
counter(); // 输出 Count: 2
counter(); // 输出 Count: 3
return 0;
}
4.在文件、函数或区域前使用static,可以限制该变量或函数的链接可见性,令其只在定义它的
文件或作用域内部可见。
// 文件 A.cpp
static void helper() {
std::cout << "This is a static function." << std::endl;
}
void publicFunction() {
helper(); // 可以调用
}
// 文件 B.cpp
// void anotherFunction() {
// helper(); // 错误,无法访问 A.cpp 中的 static 函数
// }
2. static
关键字在 C++ 中具有多种用途:
用于定义类的静态数据成员和静态成员函数。
用于保持局部变量在函数调用之间的状态。
用于限制变量和函数的可见性,确保它们仅在定义它们的文件或作用域内可见。
六、进程间通信的方式有哪些?线程间的数据交互?
6.1 进程间通信方式包括:
1.管道(Pipes)
2.命名管道(FIFO)
3.信号(Signals】
4.消息队列(Message Queues)
5.共享内存(Shared Memory)
6.信号量(Semaphores)
7.套接字(Sockets)】
8.内存映射文件(Memory-mapped files)
6.2 线程间的数据交互可以通过:
1.锁(如互斥锁Mutex)
2.条件变量(Condition Variables)
3.信号量(Semaphores)
4.线程局部存储(Thread-local Storage,TLS)
5.全局变量(通过使用同步机制以避免并发访问问题)