返回信息流对象保存在vector里,用new的方法创建的,push进vector以后就delete掉了
然后经常做的一个操作是遍历vector中的对象,满足删除条件时就把该对象erase掉,但是vector的删除只是说该对象不在vector里了吧,需不需要另外delete掉这个对象?或者说出了作用域,这个对象占用的内存就自动被回收了?
让析构函数在被调用时输出一段内容,发现在push进vector之后的那一步delete,析构函数就被调用了,但vector里的对象还存在啊,难道是副本?
另外在局部作用域那些申请的指针,没用new方法申请,应该是出了作用域就被回收了吧,用完直接让指针等于NULL就可以?
就怕有些东西没回收,一开始运行没事儿,程序连续运行几天的话就野指针满天飞内存占用惊人
这是一条镜像帖。来源:北邮人论坛 / cpp / #93153同步于 2016/8/30
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
请教一个简单的对象销毁的问题
jokenliv
2016/8/30镜像同步53 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
觉得你说的“对象保存在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出来的存储空间呢?
暖神换头像了呀
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(存储空间)。比如:
: ...................
【 在 jokenliv 的大作中提到: 】
: 暖神换头像了呀
: vector<ClassName> total;
: ClassName *tmp = new ClassName();
: ...................
/*
你new出来的object,分配在堆上,生存周期是从new开始,到delete就截止了。你往vector里存的只是它的指针。按你这个代码的写法,刚把指针放进vector,这个object就因delete而被析构了,放到vector里的那个指针就成了悬垂指针。最后的结果,你那个vector里只是存着无效的指针而已,object并不在vector里。
*/ 对不起,我错了,请无视这贴
额,我也纳闷,因为程序功能正常,看起来vector里的内容也正常啊
【 在 nuanyangyang 的大作中提到: 】
: 你new出来的object,分配在堆上,生存周期是从new开始,到delete就截止了。你往vector里存的只是它的指针。按你这个代码的写法,刚把指针放进vector,这个object就因delete而被析构了,放到vector里的那个指针就成了悬垂指针。最后的结果,你那个vector里只是存着无效的指针而已,object并不在vector里。
【 在 jokenliv 的大作中提到: 】
: 额,我也纳闷,因为程序功能正常,看起来vector里的内容也正常啊
:
访问“无效的指针”是“未定义行为”,也就是“什么都可能发生,从什么都正常到机器冒烟都是可能的”,当然,“看起来内容正常”也是可能看到的,但并不意味着程序是正确的。不过,楼主那段代码没错,尽管new和delete不是很必要。
这就是C/C++的可怕之处。如果想用好C/C++,必须对object(存储空间)的概念非常非常非常了解。否则,如果喜欢垃圾回收的话可以用Java,如果由于实际原因无法使用垃圾回收,建议使用基于“所有制”的rust,rust才是真正继承C++的RAII精髓的语言。用Rust甚至根本写不出你这样的代码。
继续安利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指向的那块堆内存这时候释放。
}
```
顺便安利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指向的存储空间。
}
```
补充一下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里的对象还存在啊,难道是副本?
: ...................