Here is a rewrite of your class, without object copying, and using the C++ standard library. The example below uses std::vector
along with move-semantics to avoid copying the data. Comments come after the code sample:
#include <vector>
#include <numeric>
#include <iostream>
class Neuron
{
private:
std::vector<double> weights; // We use a vector of doubles, not an array
double bias;
public:
Neuron(std::vector<double>&, double);
double forward_prop(const std::vector<double>&);
};
Neuron::Neuron(std::vector<double>& weights_, double bias_) : weights(std::move(weights_)), bias(bias_) // The std::move avoids the copy
{}
double Neuron::forward_prop(const std::vector<double>& values)
{
// the std::inner_product function does what your code is doing now
return std::inner_product(weights.begin(), weights.end(), values.begin(), bias);
}
int main()
{
std::vector<double> myWeights {1,2,3,4};
Neuron n(myWeights, 10);
std::cout << n.forward_prop({5,6,7,8});
}
Output:
80
Here are the key points:
The std::vector
is used instead of arrays and pointers. Note that we no longer need a num_weights
member variable, since a vector knows its own size already by calling the size()
member function of std::vector
.
In the Neuron
constructor, we issue a call to std::move
when we're assigning the passed-in vector to the objects' member. This invokes the move-semantics that are built into std::vector
. Basically the pointer(s) that the source vector uses are "stolen" or moved into the destination vector, thus avoiding the copy.
Note that the source vector will be changed when this is done. The passed-in vector will essentially become empty (remember that the contents of it were moved out).
To top things off, I implemented your forward_prop
using std::inner_product.
Additional items -- note the usage of the member-initialization list when writing the constructors. Also, the call to forward_prop
utilizes the brace-initialization syntax of std::vector
, that's why when we called forward_prop
, there was no need to explicitly declare a std::vector<double>
.
Note that the real point to take away from this is that starting with C++11, move-semantics were formally introduced into the language. This allows moving (instead of copying) to be done in a formal way.
With move-semantics now possible, additional functions were added (such as std::move
, move constructors and move-assignment operators, emplace_back
member functions in some STL container classes, std::unique_ptr
works instead of the somewhat broken std::auto_ptr
, etc.).
Before C++11, you had to do pointer "tricks" or hope that the compiler's optimizer implicitly did moving (even though there was no formal name for this type of operation).