在C++中处理类间内存释放

我在机器学习课程中使用C++实现人工神经网络。以下是我的“神经元”代码 –

struct Neuron{    float *inputs;          // 指向输入数组的指针    int inputCount;         // 输入数量    float *weights;    float sum;              // 输入的线性权重和    float output = 0.0;     // 激活函数的输出    float bias = 1.0;    float delta;            // 误差梯度    Neuron(){}    Neuron(int inputCount)    {        this->inputCount = inputCount;        this->weights = new float [inputCount];        InitializeWeights();    }    float ActivationFunction(float x)    {        return 1.0 / (1.0 + exp(-x));    }    void Calculate(float *inputs)    {        this->inputs = inputs;        float temp = 0.0;        for(int i=0; i<inputCount; i++)        {            temp += weights[i] * inputs[i];        }        temp += bias;        sum = temp;        output = ActivationFunction(sum);    }    void InitializeWeights()    {        for(int i=0; i<inputCount; i++)        {            weights[i] = rand() % 101 / 100;        }    }    ~Neuron()    {        delete[] weights;    }};

我还有另一个名为“Layer”的结构体,用于表示一个层。神经元在那里初始化如下 –

for(int i=0; i<neuronCount; i++){    neurons[i] = Neuron(inputCount);}

其中“neuronCount”是层中的神经元数量。问题是神经元中的权重数组会在析构函数调用时立即被释放。我应该如何防止这种情况发生?更重要的是,有没有更好的设计程序的方法?


回答:

你的代码存在几个问题。

首先,你在默认构造函数中没有初始化inputweights指针。因此,以下简单的程序会导致问题:

int main(){  Neuron n;}

因为n的析构函数会尝试对未初始化的指针weights调用delete []

另一个问题是你的Neuron类不能安全地复制或赋值。以下程序也会显示内存泄漏和双重删除错误的问题:

int main(){    Neuron n(10);    Neuron n2 = n;  // 复制构造函数    Neuron n3(2);    n = n3;         // 赋值}   // 一旦main()退出,就会出现内存泄漏和双重删除问题。

第三个问题是你在Calculate函数中传递了一个float指针,你不知道这个指针在函数内部是否有效,或者即使它是有效的,inputs指针代表了多少个项目。如果数量少于weights的数量,你的代码在尝试访问inputs[i]时会发生内存溢出,一旦i超出了input的界限。

为了解决这些问题,最简单的方法是使用std::vector

#include <vector>#include <algorithm>#include <cmath>#include <cstdlib>struct Neuron{    std::vector<float> inputs;          // 指向输入数组的指针    std::vector<float> weights;    float sum = 0;              // 输入的线性权重和    float output = 0.0;     // 激活函数的输出    float bias = 1.0;    float delta = 0;            // 误差梯度    Neuron() {}    Neuron(int inputCount) : weights(inputCount)     {        InitializeWeights();    }    float ActivationFunction(float x)    {        return 1.0 / (1.0 + exp(-x));    }    void Calculate(const std::vector<float>& inputs_)    {        inputs = inputs_;        // 确保向量大小相同。如果inputs较小,        // 新添加的元素将为0        inputs.resize(weights.size());        // 使用inner_product获取乘积的和。        sum = std::inner_product(std::begin(weights),                                  std::end(weights),                                  std::begin(inputs), 0.0f) + bias;        output = ActivationFunction(sum);    }    void InitializeWeights()    {        std::generate(std::begin(weights), std::end(weights), rand() % 101 / 100);    }};

请注意,我们不再使用指针,因为std::vector负责内存管理。我们也不需要有跟踪计数的成员变量(如inputCount),因为向量可以通过vector::size()知道自己的大小。

此外,std::inner_product用于在Calculate函数中生成和(在调整input向量大小与weights相同之后)。inputs_作为std::vector传递给Calculate,因此你可以使用大括号初始化列表调用它:

someInstance.Calculate({9.8, 5.6, 4.5}); // 使用包含3个项目的向量调用Calculate。

此外,InitializeWeights函数调用std::generate算法函数来设置weights向量。


如果你不使用std::vector,那么你的类将需要一个复制构造函数和赋值运算符,遵循三原则。我不会详细说明如何在不使用vector的情况下修复你的类,因为答案会变得更加复杂。


最后一点是,在C++中使用rand()应该被替换为使用C++11随机数设施

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注