BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #93153同步于 2016/8/30
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

请教一个简单的对象销毁的问题

jokenliv
2016/8/30镜像同步53 回复
对象保存在vector里,用new的方法创建的,push进vector以后就delete掉了 然后经常做的一个操作是遍历vector中的对象,满足删除条件时就把该对象erase掉,但是vector的删除只是说该对象不在vector里了吧,需不需要另外delete掉这个对象?或者说出了作用域,这个对象占用的内存就自动被回收了? 让析构函数在被调用时输出一段内容,发现在push进vector之后的那一步delete,析构函数就被调用了,但vector里的对象还存在啊,难道是副本? 另外在局部作用域那些申请的指针,没用new方法申请,应该是出了作用域就被回收了吧,用完直接让指针等于NULL就可以? 就怕有些东西没回收,一开始运行没事儿,程序连续运行几天的话就野指针满天飞内存占用惊人
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
nuanyangyang机器人#1 · 2016/8/30
觉得你说的“对象保存在vector里”和“用new的方法创建”是矛盾的。 先普及一个概念:C/C++里都有object的概念。object的意思是“存储空间”,并不是面向对象的对象。 C/C++里,每次定义一个标识符(如果不是引用和函数的话),都会创建一个object(存储空间)。比如: int a; int b; 这里a和b是两个object。 如果你说a=b,就是把存储空间b里存着的值写入存储空间a里面去。 如果你说: int *c; c = new int; 这时候,你创建了一个存储空间,在堆里,c是指向这个存储空间的指针。 数组里,每个元素都是一个存储空间。 vector里,每个元素也都是一个存储空间。所以,vector是有能力存储object(存储空间)本身的,而不仅仅是保存指针指向这些object(存储空间)。 所以最好是想想:你到底是要用vector直接储存这些存储空间,还是存放一系列的指针,指向new出来的存储空间呢?
jokenliv机器人#2 · 2016/8/30
暖神换头像了呀 vector<ClassName> total; ClassName *tmp = new ClassName(); total.push_back(*tmp); delete tmp; 大概是这样的,vector里存的不是指针,应该就是具体的实例,最后一句delete时已经析构了,但vector里的对象还存在,然后用erase而不是用pop来删除vector里的元素,那样的话,会造成内存泄露吗 【 在 nuanyangyang 的大作中提到: 】 : 觉得你说的“对象保存在vector里”和“用new的方法创建”是矛盾的。 : 先普及一个概念:C/C++里都有object的概念。object的意思是“存储空间”,并不是面向对象的对象。 : C/C++里,每次定义一个标识符(如果不是引用和函数的话),都会创建一个object(存储空间)。比如: : ...................
nuanyangyang机器人#3 · 2016/8/30
【 在 jokenliv 的大作中提到: 】 : 暖神换头像了呀 : vector<ClassName> total; : ClassName *tmp = new ClassName(); : ................... /* 你new出来的object,分配在堆上,生存周期是从new开始,到delete就截止了。你往vector里存的只是它的指针。按你这个代码的写法,刚把指针放进vector,这个object就因delete而被析构了,放到vector里的那个指针就成了悬垂指针。最后的结果,你那个vector里只是存着无效的指针而已,object并不在vector里。 */ 对不起,我错了,请无视这贴
jokenliv机器人#4 · 2016/8/30
额,我也纳闷,因为程序功能正常,看起来vector里的内容也正常啊 【 在 nuanyangyang 的大作中提到: 】 : 你new出来的object,分配在堆上,生存周期是从new开始,到delete就截止了。你往vector里存的只是它的指针。按你这个代码的写法,刚把指针放进vector,这个object就因delete而被析构了,放到vector里的那个指针就成了悬垂指针。最后的结果,你那个vector里只是存着无效的指针而已,object并不在vector里。
nuanyangyang机器人#5 · 2016/8/30
【 在 jokenliv 的大作中提到: 】 : 额,我也纳闷,因为程序功能正常,看起来vector里的内容也正常啊 : 访问“无效的指针”是“未定义行为”,也就是“什么都可能发生,从什么都正常到机器冒烟都是可能的”,当然,“看起来内容正常”也是可能看到的,但并不意味着程序是正确的。不过,楼主那段代码没错,尽管new和delete不是很必要。 这就是C/C++的可怕之处。如果想用好C/C++,必须对object(存储空间)的概念非常非常非常了解。否则,如果喜欢垃圾回收的话可以用Java,如果由于实际原因无法使用垃圾回收,建议使用基于“所有制”的rust,rust才是真正继承C++的RAII精髓的语言。用Rust甚至根本写不出你这样的代码。
nuanyangyang机器人#6 · 2016/8/30
继续安利Rust大法: ```rust struct Foo { value: i32 } fn main() { // 一个Vec // 里面的元素是Box<Foo>,即指向堆里的Foo的指针。 let mut total: Vec<Box<Foo>> = Vec::new(); { // 创建一个堆里的Foo,并初始化。 let foo0: Box<Foo> = Box::new(Foo { value:41 }); println!("{}", foo0.value); // Box“拥有”它指向的内存。 // 如果这个Box死了(比如离开了作用域),它指向的内存空间就被回收了。 } // 在这里,foo0超出了作用域,堆内存自动回收。 // 再创建一个堆里的Foo,并初始化。 let foo1: Box<Foo> = Box::new(Foo { value:42 }); println!("{}", foo1.value); // 把这个指针放进total里。这一步会转移这个指针的所有权,即“move语义”。 total.push(foo1); // 现在total拥有这块堆内存。所以不会释放的。 // println!("{}", foo1.value); // 这一行会出错!!! // foo1的所有权已经移走了,不能再使用了。 // Rust阻止了“把它放到vec里然后还想操作它”的企图。 // 要想访问还是可以的,可以“借”引用: for foo in &total { println!("{}", foo.value); } total.pop(); // 这一句从total里拿出来一个Box,但又没有把它赋给局部变量。 // 这样,那个Box“没人要”了,也就死了。原来foo1指向的那块堆内存这时候释放。 } ```
nuanyangyang机器人#7 · 2016/8/30
顺便安利C++11的unique_ptr大法: ```cpp #include <memory> // unique_ptr, make_unique #include <cstdio> #include <vector> using namespace std; class Foo { int v; public: Foo(int _v): v(_v){} ~Foo() { printf("啊,我死了!\n"); } }; int main() { vector<unique_ptr<Foo>> total; { unique_ptr<Foo> foo0 = make_unique<Foo>(41); // unique_ptr<T>就相当于Rust的Box<T>,也是指向堆内存,但“拥有”该内存。 printf("%d\n", foo0->v); } // 这里,foo0超出作用域,死了。这时候unique_ptr会free它指向的内存。 unique_ptr<Foo> foo1 = make_unique<Foo>(42); total.push_back(foo1); // unique_ptr的传递、赋值都是“move语义”,这里会将foo1指向的存储空间的所有权转移给total里的unique_ptr<Foo>型元素。 // printf("%d\n", foo1->v); // 这句也会出错,原因也是foo1被移走了。 // 当然,和rust一样,借引用是可以的: for (auto &foo : total) { printf("%d\n", foo->v); } total.pop_back(); // unique_ptr被拿出来,又没有赋给变量,所以unique_ptr死了,这时候释放原foo1指向的存储空间。 } ```
hhhhhhhhik机器人#8 · 2016/8/30
看stl的实现 虽然形参是引用,push_back内部实现还是会调用对象的拷贝构造
Silencecjx机器人#9 · 2016/8/30
补充一下8L的回答 gcc 2.95.3 <stl_vector.h> ``` void push_back(const _Tp& __x) { if (_M_finish != _M_end_of_storage) { construct(_M_finish, __x); ++_M_finish; } else _M_insert_aux(end(), __x); } ``` 一个简单的 Test ``` class Name { public: Name(int id = 0) : id_(id) { } int id_; }; void test_vector() { std::vector<Name> names; Name *tmp = new Name(); names.push_back(*tmp); delete tmp; for (auto name : names) { std::cout << name.id_ << std::endl; } } ``` Output: 0 确实是调用了ctor,就算 delete tmp,vector 中的 object 不会受到影响。 当然内存管理还是暖神说的 smart pointer 大法好,跟团学习了。 【 在 jokenliv 的大作中提到: 】 : 对象保存在vector里,用new的方法创建的,push进vector以后就delete掉了 : 然后经常做的一个操作是遍历vector中的对象,满足删除条件时就把该对象erase掉,但是vector的删除只是说该对象不在vector里了吧,需不需要另外delete掉这个对象?或者说出了作用域,这个对象占用的内存就自动被回收了? : 让析构函数在被调用时输出一段内容,发现在push进vector之后的那一步delete,析构函数就被调用了,但vector里的对象还存在啊,难道是副本? : ...................