C++ 为什么向向量添加智能指针会导致内存泄漏?

C++ 为什么向向量添加智能指针会导致内存泄漏?,c++,vector,memory-leaks,smart-pointers,C++,Vector,Memory Leaks,Smart Pointers,我试图生成要添加到向量的对象,以用于填充数据库,在这样做时,我注意到内存泄漏, 我确信这与我将对象添加到向量的方式有关,因为如果我不将任何对象添加到向量,函数将按预期工作,不会泄漏。我哪里出错了 我尝试了独特的ptr,同样的结果 我发现有了这段代码,并不是所有的内存都会在每次运行时被取消分配 数据库::populate,在多次执行populate函数后,几乎400 mb的内存不会被取消分配,为什么 main.cpp #include <iostream> #include "data

我试图生成要添加到向量的对象,以用于填充数据库,在这样做时,我注意到内存泄漏, 我确信这与我将对象添加到向量的方式有关,因为如果我不将任何对象添加到向量,函数将按预期工作,不会泄漏。我哪里出错了

我尝试了独特的ptr,同样的结果

我发现有了这段代码,并不是所有的内存都会在每次运行时被取消分配 数据库::populate,在多次执行populate函数后,几乎400 mb的内存不会被取消分配,为什么

main.cpp

#include <iostream>
#include "database.h"
int main()
{
    std::vector<string> cols{"name", "score" };
    while (true) {
        std::getchar();
        database::populate("test", cols, 1000000);
 }

}

#include "database.h"
#include "model.h"

#include <memory>
#include <random>
#include <iostream>

typedef std::vector<std::shared_ptr<Model>> modelContainer;

static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
//temporary
int alphaLen = std::size(alphanum) - 1;
std::random_device rd;
std::mt19937 gen(rd());
int random(int min, int max) {
    std::uniform_int_distribution<>rng_dice(min, max); return rng_dice(gen);
}
string randstring(int len) {
    string  str = "";

    while (len--) {
        int index = random(0, alphaLen);
        str += alphanum[index];
    }
    return str;
}
void database::populate(const string table, std::vector<string> cols, int limit) {
    modelContainer models;
    std::shared_ptr<Model> e;
    for (int i = 0; i < limit; i++) {
        e = std::make_shared<Model>(table);
        for (int j = 0; j < cols.size(); j++) {
            e->addVal(cols[j], randstring(5));
        }
        models.push_back(std::move(e)); //Leak??

    }
    std::cout << "done!" << std::endl;

    // add to database
}
#include "model.h"
#include <string>
Model::Model(const string table) {
    this->table = table;


}
void Model::addVal(const string& key, const string& val) {
    (data)[key] = val;
}
#包括
#包括“database.h”
int main()
{
std::向量列{“name”,“score”};
while(true){
std::getchar();
数据库::填充(“测试”,cols,1000000);
}
}
数据库.h

#include <string>
#include <vector>
using std::string;
class database {

public:

    static void  populate(const string table, std::vector<string> cols, int limit);
};
#include <map>
#include<string>
#include <vector>
using std::string;
class Model {
private:
    std::map<string, string> data;
    string table;
public:
    Model(const string table);
    void addVal(const string& key, const string& val);
};
#包括
#包括
使用std::string;
类数据库{
公众:
静态void填充(常量字符串表,std::vector cols,int limit);
};
数据库.cpp

#include <iostream>
#include "database.h"
int main()
{
    std::vector<string> cols{"name", "score" };
    while (true) {
        std::getchar();
        database::populate("test", cols, 1000000);
 }

}

#include "database.h"
#include "model.h"

#include <memory>
#include <random>
#include <iostream>

typedef std::vector<std::shared_ptr<Model>> modelContainer;

static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
//temporary
int alphaLen = std::size(alphanum) - 1;
std::random_device rd;
std::mt19937 gen(rd());
int random(int min, int max) {
    std::uniform_int_distribution<>rng_dice(min, max); return rng_dice(gen);
}
string randstring(int len) {
    string  str = "";

    while (len--) {
        int index = random(0, alphaLen);
        str += alphanum[index];
    }
    return str;
}
void database::populate(const string table, std::vector<string> cols, int limit) {
    modelContainer models;
    std::shared_ptr<Model> e;
    for (int i = 0; i < limit; i++) {
        e = std::make_shared<Model>(table);
        for (int j = 0; j < cols.size(); j++) {
            e->addVal(cols[j], randstring(5));
        }
        models.push_back(std::move(e)); //Leak??

    }
    std::cout << "done!" << std::endl;

    // add to database
}
#include "model.h"
#include <string>
Model::Model(const string table) {
    this->table = table;


}
void Model::addVal(const string& key, const string& val) {
    (data)[key] = val;
}
#包括“database.h”
#包括“model.h”
#包括
#包括
#包括
typedef std::向量模型容器;
静态常量字符alphanum[]=
"0123456789"
“ABCDEFGHIJKLMNOPQRSTUVWXYZ”
“abcdefghijklmnopqrstuvwxyz”;
//暂时的
int alphaLen=std::size(alphanum)-1;
std::随机_装置rd;
标准:mt19937 gen(rd());
int随机(int最小值,int最大值){
标准::统一内部分配规则(最小值、最大值);返回规则(一般值);
}
字符串和字符串(整数长度){
字符串str=“”;
而(len--){
int索引=随机(0,alphaLen);
str+=alphanum[索引];
}
返回str;
}
void database::populate(常量字符串表,std::vector cols,int limit){
模型容器模型;
std::共享的ptr e;
对于(int i=0;iaddVal(cols[j],randstring(5));
}
models.push_back(std::move(e));//泄漏??
}

std::cout在查看
任务管理器或
top
时,您的操作系统不会准确报告您分配/释放的内存,因此我不会开始担心该程序中的内存消耗,除非它随时间增加。GNU有一个名为
malloc_stats()的函数形式的扩展
您可以在程序中调用以报告malloc/free统计信息

我扫描了你的程序(使用扫描构建、地址消毒器、ubsan等),但没有发现任何问题,并且在无休止的循环中运行时内存消耗保持在同一水平。我使用
malloc\u stats
检查后者


由于我已经通读了代码,因此需要注意以下几点:

  • 使用页眉护罩:
    #ifndef header_H
    …或
    #pragma一次
  • 不要在头文件的全局名称空间中使用…
。这样的头文件的用户可能会感到惊讶
  • 通过
    const&
    而不是通过值复制代价高昂的参数。当前存在一些不必要的
    string
    vector
    复制
  • 初始化构造函数成员初始值设定项列表中的类成员变量。例如
    Model::Model(常量字符串和表):数据{},表(表){/*现在为空*/}
  • 使用下标运算符时请使用正确的类型。您使用了有符号整数(
    int
    ),这会触发警告,而无符号整数(
    size\t
    )更合适
  • 当您知道要添加多少条目时,在支持它的容器上使用
    reserve()
  • 如果您不需要共享,请选择
    唯一的\u ptr
    而不是
    共享的\u ptr
  • 与存储对象的唯一性相比,更喜欢让标准容器存储实际对象
  • uniform\u int\u发行版
    非常轻量级,但是如果您一直只需要相同的发行版,那么您最好重用它
  • std::mt19937
    不是线程安全的,因此如果要从多个线程使用它,请将其设置为
    thread\u local
    。这在当前代码中不是问题,但最好知道
  • 数据库.cpp示例

    #include "database.h"
    #include "model.h"
    
    #include <iostream>
    #include <memory>
    #include <random>
    #include <string_view>
    
    using namespace std::literals::string_view_literals;
    using std::string;
    using modelContainer = std::vector<Model>; // no smart pointer needed
    
    // A function to return a thread_local generator that is initialized once.
    inline std::mt19937& generator() {
        // The std::random_device is created, called and disposed of since
        // we only need it to seed the std::mt19937 PRNG.
        static thread_local std::mt19937 gen(std::random_device{}());
        return gen;
    }
    
    // Made random into a function template to make it able to return the
    // correct type and taking [min, max] as template parameters to only
    // instantiate the distribution once per IntType and range used.
    template<typename IntType, IntType min, IntType max>
    inline IntType random() {
        static std::uniform_int_distribution<IntType> rng_dice(min, max);
        return rng_dice(generator());
    }
    
    string randstring(size_t len) {
        static constexpr std::string_view alphanum =
            "0123456789"
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz"sv; // sv = string_view literal
    
        string str;
        str.reserve(len);
        while(len--) {
            auto index = random<size_t, 0, alphanum.size() - 1>();
            str += alphanum[index];
        }
        return str;
    }
    
    void database::populate(const string& table, const std::vector<string>& cols,
                            size_t limit) {
        modelContainer models;
        models.reserve(limit);
        for(size_t i = 0; i < limit; ++i) {
            Model e(table);
            for(size_t j = 0; j < cols.size(); ++j) {
                e.addVal(cols[j], randstring(5));
            }
            models.emplace_back(std::move(e));
        }
        std::cout << "done!" << std::endl;
    }
    
    #包括“database.h”
    #包括“model.h”
    #包括
    #包括
    #包括
    #包括
    使用名称空间std::literals::string\u view\u literals;
    使用std::string;
    使用modelContainer=std::vector;//不需要智能指针
    //一个函数,用于返回初始化一次的线程本地生成器。
    内联标准::mt19937和生成器(){
    //std::random_设备是从
    //我们只需要它来播种std::mt19937 PRNG。
    静态线程_local std::mt19937 gen(std::random_device{}());
    返回发电机;
    }
    //随机添加到函数模板中,使其能够返回
    //正确的类型并将[min,max]作为模板参数仅用于
    //每个使用的IntType和range实例化一次分发。
    模板
    内联IntType random(){
    静态标准:均匀分布(最小值、最大值);
    返回rng_骰子(生成器());
    }
    字符串和字符串(大小长度){
    静态constexpr std::string\u视图alphanum=
    "0123456789"
    “ABCDEFGHIJKLMNOPQRSTUVWXYZ”
    “abcdefghijklmnopqrstuvwxyz”sv;//sv=string\u view literal
    字符串str;
    str.reserve(len);
    而(len--){
    自动索引=随机();
    str+=alphanum[索引];
    }
    返回str;
    }
    void database::populate(常量字符串和表,常量std::vector和cols,
    尺寸(不限){
    模型容器模型;
    模型.储备(限额);
    对于(尺寸i=0;i