new 和 malloc | from web

本文详细对比了C++中new与C/C++中malloc的功能及使用方法。new不仅可以分配内存还能调用构造函数初始化对象,而malloc仅负责内存分配。此外,new在类型安全和内存管理方面提供了更多的保障。
 

new与malloc

1.malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存

2.对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。

3.因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
4.C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
5.new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void*指针。

new delete在实现上其实调用了malloc,free函数

6.new建立的对象你可以把它当成一个普通的对象,用成员函数访问,不要直接访问它的地址空间;malloc分配的是一块内存区域,就用指针访问好了,而且还可以在里面移动指针.

7.new 建立的是一个对象;alloc分配的是一块内存.

***************************************

相同点:都可用于申请动态内存和释放内存

不同点
(1)操作对象有所不同
malloc与free是C++/C 语言的标准库函数,new/delete 是C++的运算符。对于非内部数据类的对象而言,光用maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数, 对象消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加malloc/free。

(2)在用法上也有所不同
函数malloc 的原型如下:
void * malloc(size_t size);
用malloc 申请一块长度为length 的整数类型的内存,程序如下:
int *p = (int *) malloc(sizeof(int) * length);
我们应当把注意力集中在两个要素上:“类型转换”和“sizeof”。
malloc 返回值的类型是void *,所以在调用malloc 时要显式地进行类型转换,将void * 转换成所需要的指针类型。
malloc 函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。

函数free 的原型如下:
void free( void * memblock );
为什么free 函数不象malloc 函数那样复杂呢?这是因为指针p 的类型以及它所指的内存的容量事先都是知道的,语句free(p)能正确地释放内存。如果p 是NULL 指针,那么free

对p 无论操作多少次都不会出问题。如果p 不是NULL 指针,那么free 对p连续操作两次就会导致程序运行错误。

new/delete 的使用要点
运算符new 使用起来要比函数malloc 简单得多,例如:
int *p1 = (int *)malloc(sizeof(int) * length);
int *p2 = new int[length];
这是因为new 内置了sizeof、类型转换和类型安全检查功能。对于非内部数据类型的对象而言,new 在创建动态对象的同时完成了初始化工作。如果对象有多个构造函数,那么new 的语句也可以有多种形式。

如果用new 创建对象数组,那么只能使用对象的无参数构造函数。例如
Obj *objects = new Obj[100]; // 创建100 个动态对象
不能写成
Obj *objects = new Obj[100](1);// 创建100 个动态对象的同时赋初值1
在用delete 释放对象数组时,留意不要丢了符号‘[]’。例如
delete []objects; // 正确的用法
delete objects; // 错误的用法
后者相当于delete objects[0],漏掉了另外99 个对象。

***************************************

1  new自动计算需要分配的空间,而malloc需要手工计算字节数
2  new是类型安全的,而malloc不是,比如:
int* p = new float[2]; // 编译时指出错误
int* p = malloc(2*sizeof(float)); // 编译时无法指出错误
new operator 由两步构成,分别是 operator new 和 construct
3  operator new对应于malloc,但operator new可以重载,可以自定义内存分配策略,甚至不做内存分配,甚至分配到非内存设备上。而malloc无能为力
4  new将调用constructor,而malloc不能;delete将调用destructor,而free不能。
5  malloc/free要库文件支持,new/delete则不要。

分析下这段多线程服务器代码:#include "web_server.h" // 线程处理函数 void *thread_func(void *arg) { int client_fd = *(int *)arg; free(arg); pthread_detach(pthread_self()); handle_client(client_fd); return NULL; } // 多线程服务器 void thread_server() { int server_fd, client_fd; struct sockaddr_in server_addr, client_addr; socklen_t client_len = sizeof(client_addr); pthread_t thread_id; // 创建socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(EXIT_FAILURE); } // 设置socket选项 int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))){ perror("setsockopt"); exit(EXIT_FAILURE); } // 绑定地址 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = INADDR_ANY; memset(&(server_addr.sin_zero), '\0', 8); if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) ){ perror("bind"); exit(EXIT_FAILURE); } // 监听 if (listen(server_fd, BACKLOG) == -1) { perror("listen"); exit(EXIT_FAILURE); } printf("Thread server listening on port %d...\n", PORT); // 主循环 while (1) { client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd == -1) { perror("accept"); continue; } printf("Accepted connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); int *new_client_fd = malloc(sizeof(int)); *new_client_fd = client_fd; if (pthread_create(&thread_id, NULL, thread_func, new_client_fd) != 0) { perror("pthread_create"); close(client_fd); free(new_client_fd); } } close(server_fd); } int main() { thread_server(); return 0; }
最新发布
08-12
<think>我们分析这段多线程服务器代码,主要关注其线程模型、资源管理、错误处理等方面。 代码结构: 1. 线程处理函数 thread_func 2. 多线程服务器主函数 thread_server 3. main函数调用thread_server 详细解析: 1. 线程处理函数 thread_func: - 参数:void *arg,实际上是一个指向客户端文件描述符的指针(int*) - 函数内部: int client_fd = *(int *)arg; // 获取客户端文件描述符 free(arg); // 释放动态分配的内存(重要,避免内存泄漏) pthread_detach(pthread_self()); // 将线程设置为分离状态(线程结束自动回收资源) handle_client(client_fd); // 处理客户端请求 return NULL; // 线程结束 注意:线程分离后,不需要主线程调用pthread_join来回收资源。 2. 多线程服务器主函数 thread_server: - 创建服务器套接字,设置SO_REUSEADDR选项(允许地址重用,避免端口占用) - 绑定地址(INADDR_ANY表示任意网络接口)端口 - 监听连接 - 主循环:不断接受客户端连接,并为每个客户端创建一个新线程处理。 关键步骤: client_fd = accept(...); // 接受客户端连接 // 动态分配内存存储客户端文件描述符(避免多线程竞争) int *new_client_fd = malloc(sizeof(int)); *new_client_fd = client_fd; // 创建线程,将动态分配的文件描述符指针传递给线程函数 pthread_create(&thread_id, NULL, thread_func, new_client_fd); 错误处理: - 创建线程失败时,关闭客户端连接并释放动态分配的内存。 3. 资源管理: - 在创建线程时,动态分配内存存储客户端文件描述符(int*),这样每个线程都有自己的副本,避免竞争。 - 在线程函数中,立即释放这块内存(free(arg)),防止内存泄漏。 - 线程设置为分离状态(pthread_detach),这样线程结束时资源会自动回收,无需主线程管理。 4. 并发模型: - 主线程负责接受连接(accept),每次接受一个连接就创建一个新线程处理。 - 每个客户端连接由一个独立的线程处理(handle_client函数),实现并发。 5. 潜在问题: - 线程创建开销:对于高并发场景,频繁创建线程可能消耗大量资源(每个线程需要独立的栈空间等)。 - 未限制最大线程数:可能导致系统资源耗尽(如内存不足,线程创建失败)。 - 动态内存分配:每次连接都malloc,虽然及时free,但频繁分配可能影响性能。 6. 改进建议: - 使用线程池:预先创建一组线程,避免频繁创建销毁的开销。 - 加入最大连接数限制,防止资源耗尽。 - 考虑使用连接队列,当线程池满时拒绝新连接。 7. 与多进程模型的对比: - 线程模型共享地址空间,资源消耗相对较小,但需要处理线程安全问题。 - 进程模型更健壮(一个进程崩溃不影响其他进程),但资源消耗大。 8. 错误处理: - 对socket、bind、listen、accept等系统调用进行了错误检查,并打印错误信息。 - 但accept出错后使用continue继续循环,保证服务器不会因单个错误退出。 总结: 这段代码实现了一个基本的多线程服务器,能够同时处理多个客户端连接。通过动态分配内存传递文件描述符、设置线程分离,避免了资源泄漏。但存在高并发下的可扩展性问题,适合连接数不高的场景。 §§相关问题§§ 1. 为什么在传递客户端文件描述符时需要动态分配内存(malloc)而不是直接传递栈上的变量地址? 2. 如果线程创建失败(pthread_create返回非0),除了关闭连接释放内存外,还需要考虑哪些处理措施? 3. 在多线程服务器中,handle_client函数需要满足什么条件才能保证线程安全? 4. 如何修改代码以引入线程池机制来优化性能? 5. 当服务器需要同时处理数千个客户端连接时,多线程模型可能遇到哪些瓶颈?有哪些替代方案?</think>### 多线程服务器代码深度分析 #### 核心架构设计 ```mermaid graph LR A[主线程] --> B[创建监听套接字] B --> C[设置SO_REUSEADDR] C --> D[绑定端口] D --> E[开始监听] E --> F{循环等待连接} F -->|新连接| G[动态分配client_fd内存] G --> H[创建新线程] H --> I[线程处理函数] I --> J[处理客户端请求] ``` #### 关键代码解析 1. **线程处理函数**: ```c void *thread_func(void *arg) { int client_fd = *(int *)arg; // 获取客户端描述符 free(arg); // 释放动态分配的内存 pthread_detach(pthread_self()); // 设置线程分离 handle_client(client_fd); // 核心业务处理 return NULL; } ``` - **内存管理**:通过`malloc/free`避免线程间描述符冲突 - **线程分离**:`pthread_detach`确保线程结束后自动回收资源 - **业务隔离**:每个线程独立处理客户端请求 2. **连接处理流程**: ```c while (1) { client_fd = accept(...); // 接受新连接 int *new_client_fd = malloc(sizeof(int)); // 动态分配内存 *new_client_fd = client_fd; // 存储描述符 pthread_create(...); // 创建线程处理 } ``` - **动态内存分配**:避免主线程子线程共享栈变量 - **错误处理**:创建线程失败时关闭连接并释放内存 - **持续监听**:`while(1)`保证服务持续运行 #### 关键技术点解析 1. **SO_REUSEADDR选项**: ```c setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) ``` - 允许快速重启服务器(避免"Address already in use"错误) - 关键值`opt=1`启用该选项 2. **线程分离机制**: - `pthread_detach(pthread_self())`使线程成为"分离状态" - 优点:线程终止时自动释放资源 - 替代方案:主线程使用`pthread_join`回收(需维护线程ID列表) 3. **内存管理策略**: | 操作 | 位置 | 目的 | |------|------|------| | malloc | 主线程 | 为每个连接分配独立内存 | | free | 子线程 | 立即释放避免泄漏 | | 值传递 | 通过指针 | 避免描述符复制冲突 | 4. **并发模型对比**: | 特性 | 多进程模型 | 多线程模型 | |------|------------|------------| | 资源开销 | 高(独立地址空间) | 低(共享地址空间) | | 创建速度 | 慢(需复制进程) | 快(仅创建线程) | | 通信成本 | 高(需IPC) | 低(共享内存) | | 隔离性 | 强(崩溃不影响) | 弱(线程崩溃影响全局) | #### 潜在问题与优化建议 1. **资源限制风险**: - 无最大线程数限制 → 可能导致资源耗尽 - 优化:添加线程池机制 2. **内存泄漏隐患**: - `pthread_create`失败时需`free(new_client_fd)` - 当前实现已正确处理 3. **性能瓶颈**: - 高频连接时`malloc/free`可能成为瓶颈 - 优化:预分配内存池 4. **线程安全问题**: - `handle_client`需确保线程安全 - 避免使用全局变量或添加互斥锁 #### 完整工作流程 1. 主线程创建监听套接字 2. 循环接受客户端连接 3. 为每个连接动态分配内存存储client_fd 4. 创建分离线程处理请求 5. 子线程立即释放内存并处理业务 6. 线程结束自动回收资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值