在C++中处理类间内存分配

Handling Inter Class Memory Deallocation in C++

本文关键字:内存 分配 处理 C++      更新时间:2023-10-16

我正在为我的机器学习课程用C++实现人工神经网络。这是我的"神经元"代码-

struct Neuron
{
float *inputs;          // pointer to inputs array
int inputCount;         // number of input
float *weights;
float sum;              // linear weight sum of inputs
float output = 0.0;     // output from activation function
float bias = 1.0;
float delta;            // error gradient

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;  // copy constructor
Neuron n3(2);
n = n3;         // assignment
}   // memory leaks, double deletion issues once main() exits.

第三个问题是,在Calculate函数中传递一个指向float的指针,但您不知道该指针在该函数内部是否有效,甚至不知道inputs指针代表了多少项。如果这个数字小于weights的数字,那么一旦i超出input的范围,您的代码就会出现内存溢出,试图访问inputs[i]

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

#include <vector>
#include <algorithm>
#include <cmath>
#include <cstdlib>
struct Neuron
{
std::vector<float> inputs;          // pointer to inputs array
std::vector<float> weights;
float sum = 0;              // linear weight sum of inputs
float output = 0.0;     // output from activation function
float bias = 1.0;
float delta = 0;            // error gradient
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_;
// make sure vectors are the same size.  If inputs is smaller,
// the new elements added will be 0
inputs.resize(weights.size());
// use inner_product to get the sum of the products.
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}); // calls Calculate with a vector consisting of 3 items.

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


如果不使用std::vector,那么您的类将需要一个复制构造函数和赋值运算符,遵循规则3。我不会讨论在不使用vector的情况下如何修复类,因为答案会涉及更多内容。


最后一项是C++中rand()的使用应替换为C++11随机数设施的使用。