Cpp_Basic---11/N(new/delete)

2017-01-05 11:04:05来源:oschina作者:OSer_Merlin人点击

凡是谈到new/delete, 总会不落俗套的要谈malloc/calloc, free, 并且还会谈对象的构建机制, 即构造器和new, 析构器和delete, 之后高级一点儿的还要谈到, 内存池, STL中内存的分配. 不过本篇不谈高级东西, 只谈基础的.

修订历史:

初稿: (1. new/delete Vs malloc/free, 2. new/delete和对象构建机制)---2017/1/4
C++, Why new/delete?

这其实是一个很无聊的问题, 只是会用不就好了么? 如果只是这样的话, 后面让你去写一个带内存池或者基于CPU Cache的内存分配框架, 你怎么办?----当然这是后话, 总之多想想总是好的(想走捷径的人, 最终都走了远路)


结论: C++面向对象啊(构造, 清理工作想用的更爽一点儿).


相对于C++这种大对象/大变量比较多的面向对象设计语言, 它希望得到更加高层次的封装 而不是在像C那样, 几个库函数跑满天; C++是面向对象的呀, 所以就不能以函数为核心, 那么不能为面向对象的东西, 怎么搞呢? 干脆做成关键字(其实也是运算符, 因为你可以去重载new/delete, new[]/delete[])


但是哪一套不较好? 这个问题, 就好像在在争论"自动挡好, 还是手动挡好", 那我不去争, 我只说, 你看看86是手动挡还是自动挡.


过分的清晰, 就会更加关注底层实现, 以及繁琐的细节; 更高层次的东西, 越是封装的深, 但是其底层, 不过是高手帮你做这些繁琐的细节. 到底是用那种, 真的要根据你的业务细节, 甚至是硬件平台, 来使用或者创造.

malloc/free

这货出现的时候, 总是按字节分配的, 所以它现身的时候, 少不了 sizeof 关键字, 你要还去分辨一下是否分配成功, 分配的内存地址初始化了没有(calloc()的话, 稍微好点儿), 分配的指针强制类型转换了没有... 简单的案例如下:


#include
#include
#include int main(void){
struct student{
char name[10];
int age;
};
struct student *s1 = (struct student *)malloc(sizeof(struct student));
if(s1 == NULL){
printf("oh, malloc failed");
return -1;
}
memset(s1, 0, sizeof(struct student));
strcpy(s1->name, "merlin");
s1->age = 10;
printf("name=%s, age=%d/n", s1->name, s1->age);
if(s1){
free(s1);
s1 = NULL;
} return 0;
}

运行结果:


gcc -g -Wall main.c -o main
./main
name=merlin, age=10

数组怎么搞? 数组一样按照字节, sizeof就可以了.

delete/new

其实这个东西本身是很复杂的, 换句话说, 封装非常好.


你按照原来原来的方式, 按可以按照原来的方式(按字节), 也可以按照现在的方式(按类型), 稍微不同的是,当你对数组分配/释放的时候, 你可以选择使用new[], delete[]关键字(同时也是运算符)


int *p1 = new int(10);//init with number 10
int *p2 = new int[10]{0}; //an array with 10 elemsdelete p1; //u'd better judge weather it is null
delete p2;

new/detele相比原始的malloc/free, 在分配空间之后, 还可以进行相应的初始化, 特别是类型为类对象时,并且释放的时候, 也不必再手动给指针置null.



失败了怎么办?--其实new/delete本身的默认实现, 是有抛出异常的, 也建议你捕获并处理异常. 例如, operator new()的一种重载可能是这样的:


void* operator new(size_t size)
{
void* p = null
while(!(p = malloc(size)))
{
if(null == new_handler)
throw bad_alloc();
try
{
new_handler();
}
catch(bad_alloc e)
{
throw e;
}
catch(…)
{}
}
return p;
}

上面只是一种简单的实现, 就是想说, new/delete也是有安全措施的:


void* operator new (std::size_t size) throw (std::bad_alloc);

或者:


void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();

或者:


void* operator new (std::size_t size, void* ptr) throw();

所以你可能会看到这样的用法:


#include
#include
int main(void){
try{
const long size = std::numeric_limits::max();
int *ptr = new int[size];
std::cout << ptr[0] << std::endl;
}catch(const std::bad_alloc e){
std::cout << "calloc failed, cause: "<< e.what() << std::endl;
return -1;
} return 0;
}

但是就是有些人, 不喜欢写异常(防御式编程), 比如: (契约式)--依赖返回值返回的code(返回值约束)


#include
#include
int main(void){ const long size = std::numeric_limits::max();
int *ptr = new(std::nothrow) int[size];
if(!ptr){
std::cout << "calloc failed" << std::endl;
return -1;
}
std::cout << ptr[0] << std::endl;
return 0;
}

(忘记说了, 如果你的操作系统能够分配这个级别的内存而不失败, 请和我做朋友, 土豪.)

碰到数组怎么搞?


碰到数组的时候, 要悠着点儿, 特别是多维数组. 下面玩一把:


..明天再说吧.


深挖一下new

merlin


2017/1/4

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台