关于c++容器vector,push_back带有资源管理(new,malloc等)对象时需要注意的事情。拷贝,析构,作用域

本文探讨了C++标准库vector在push_back任务类对象时的潜在陷阱,特别是在资源管理中可能导致的double-free问题。通过实例代码和分析,强调了在处理涉及资源的类时,如何避免拷贝和析构带来的复杂性,以及使用vector<task_class*>替代的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于c++标准库中的vector,我相信应该是用的比较多的一种结构。读者们对此大多应该也不陌生,但是对于初学者可能仍有一些值得注意的地方,本篇文章将讨论其中一种陷阱。

我们都知道C++类的拷贝有最简单的两种区分:深拷贝和浅拷贝。当我们使用vector进行push_back类的对象时候,对于类的拷贝和析构就应该多加注意。push_back的函数原型为push_back(const value_type& __x),使用的引用,按理来说不会产生拷贝,但是当我们进一步查看push_back时候,会发现有一句语句_M_realloc_insert(end(), __x)。

原来,当我们在使用vector的方法push_back时候,首先会检查容量是否满足,当vector的容量不能满足的时候,会调用_M_realloc_insert(end(), __x),首先将原有数据进行拷贝,增加新的数据,再将原有数据进行析构。因此整个过程会产生类对象的拷贝和析构。(此处的原理不再多做描述,有兴趣的读者可进行搜查)。

当我们push_back的对象不涉及资源(如new,malloc的内存空间,文件描述符等)管理的时候,push_back对原有数据的拷贝和析构看起来无关大雅。但是一但当push_back的对象涉及到了资源管理时,push_back对原有数据的拷贝和析构就会产生很大问题。

话不多说,直接上代码,作为程序员,还是代码能给我们更多信息。头文件如下

#ifndef TEST_H

#define TEST_H

#include <vector>

using namespace std;

/*工具类,其中涉及到了最普遍的资源管理:new*/

class tool_class

{

private:

    int* pint;

public:

    tool_class();

    ~tool_class();

    void init(int value);

    void r_printf();

};

/*任务类,具有工具类tool_class成员变量new_app_class*/

class task_class

{

private:

    tool_class new_app_class;

public:

    void init(int value);

    void exe();

};

/*任务管理类,具有任务数组vector<task_class> v_arrary,存储任务*/

class management_class

{

private:

    vector<task_class> v_arrary;

public:

    management_class();

    void init();

    void exe();

};

#endif

源文件代码:

#include <iostream>

#include <string>

#include <tr1/memory>

#include <vector>

#include "test_vector.h"

/*工具类的方法定义*/

tool_class::tool_class()

{

    pint = NULL;

}

tool_class::~tool_class()

{

    if(pint != NULL)

    delete pint;

    pint = NULL;

}

void tool_class::init(int value)

{

    pint = new int;

    *pint = value;

}

void tool_class::r_printf()

{

    cout << "r_printf value start"<< endl;

    cout << "*pint = "<<*pint<< endl;

}



 

/*任务类的方法*/

void task_class::init(int value)

{

    new_app_class.init(value);

}

void task_class::exe()

{

    new_app_class.r_printf();

}


 

/*管理类的方法*/

management_class::management_class()

{

    v_arrary.clear();

}

void management_class::init()

{

    //for(int i = 0; i < 3; i++)

    //{

        task_class tmp;

        tmp.init(3);

        v_arrary.push_back(tmp);

    //}

}

void management_class::exe()

{

    for(unsigned int i = 0; i < v_arrary.size(); i++)

    {

        v_arrary[i].exe();

    }

}

int main()

{

    management_class management;

    management.init();

    management.exe();

    return 0;

}

管理类对象中我向vector中push_back了一个对象,运行结果如下:

究其原因是,push_back会进行拷贝,使用的是默认的拷贝函数。当定义的局部类对象 task_class tmp;离开作用域时候,会调用析构函数,将new的空间释放。而此时vector中任务类的工具变量任然指向这个空间,当vector离开作用域时候,会再次调用析构释放,造成free double。

而当push_back多个对象时候,情况更加难以想象(读者验证的话,可以在工具类的析构中加打印,同时删除delete,方便查看调用了多少次析构。不然会因free double中断,无法打印真实的析构调用次数)。局部中间变量对像离开作用域的析构,push_back造成的析构,都会给资源管理造成很多麻烦,尤其是当类在析构函数中释放资源时候。

而解决这种麻烦的其中一种方法是,不将类对象push_back进vector,而是将对象的指针保存进vector。vector<task_class *>的形式,其中保存的指针是由new或者malloc开辟的空间,这样数据将在手动free或delete释放之前永久有效。

而push_back造成的拷贝和析构也只是针对保存的对象指针,不涉及到指针指向的内容,就可以保证资源只由代码作者本身手动释放。

当工程代码繁多,应当小心的在类的成员变量中使用其他类。一但类的相关性牵扯较多,代码出现资源管理问题时候就很难定位。上述的方法虽然解决了问题,但是资源的开辟和释放时间之间的差距由任务的量决定,这也容易对代码的性能造成影响。

注:

文章中有错误的地方,欢迎评论区指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值