更改继承成员的地址

Changing address of inherited members


我遇到了一个奇怪的问题,我想知道 C++11 是否有关于此的特殊规则我不知道。

我有 2 个 classes :

1 - ClNeuron(摘要)
2 - ClLSTMNeuronClNeuron 的child)

声明如下:

class ClNeuron
{
    protected:

    //Initialization function
    virtual void Init(unsigned long p_uid);

    double Sigmoid(double p_value);
    double SigmoidDerivative(double p_value);

    double TanH(double p_value);
    double TanHDerivative(double p_value);

    public:

    const double CONST_DEFAULT_MOMENTUM_VALUE = 0.1;
    const double CONST_DEFAULT_LEARNING_RATE = 0.05;

    //All of the output connection of this neuron 
    std::vector<ClNeuronConnection*> m_output_connections;

    //Al of the input connection of this neuron
    std::vector<ClNeuronConnection*> m_input_connections;

    bool m_initialized;
    double m_result_buffer;

    //Error related informations
    double m_last_error_delta;  
    double m_error_gradient;

    unsigned long m_uid;
    double m_learning_rate;     

    public:

    bool m_is_bias;
    ClDataSet* m_dataset;

    virtual ~ClNeuron();
    ClNeuron(unsigned long p_uid);
    ClNeuron();


    //Connect this neuron's output to another / others neurons' input
    virtual bool AddOutputConnection(ClNeuron* p_neuron);

    //This neuron got a request to have multiple new input
    virtual std::vector<ClNeuronConnection*> InputConnectionRequest(ClNeuron* p_neuron);

    //Tell the neuron to fire the sum of the processed inputs
    virtual double Fire();
    virtual double Fire(double p_data);

    //void ComputeErrorGradient(double p_wanted_output);

    //Function updating all of the current neuron's weight of the OUTPUT connections , depending on an error ratio
    //void UpdateWeights();

    //Set the result buffer using the transfer function . NOTE : This is a pure virtual function
    virtual void ProcessInputs() = 0; 
    virtual bool ComputeErrorGradient() = 0;
    virtual void ComputeWeightDeltas();
    virtual void UpdateWeights();
    virtual void ResetContext();

    //Print neuron & connections & weights
    virtual void PrintNeuronData();
};



class ClLSTMNeuron : public ClNeuron
{
    protected:
    std::vector<ClNeuronConnection*> m_forget_gate_input_connections;
    std::vector<ClNeuronConnection*> m_input_gate_input_connections;
    std::vector<ClNeuronConnection*> m_output_gate_input_connections;
    double m_input_gate_result_buffer;
    double m_output_gate_result_buffer;
    double m_forget_gate_result_buffer;
    double m_cell_state;

    public:
    //Override the ProcessInputs function
    std::vector<ClNeuronConnection*> InputConnectionRequest(ClNeuron* p_neuron);
    void ProcessInputs();
    bool ComputeErrorGradient();
    ClLSTMNeuron();
    virtual ~ClLSTMNeuron();
};

问题如下: ClLSTMNeuron 构造函数调用它的 parent class 函数 Init() ,如下所示:

ClLSTMNeuron::ClLSTMNeuron()
{
    ClNeuron::Init(0);
    std::cout << "ClLSTMNeuron::ClLSTMNeuron() [" << this << "]: my OC [" << &this->m_output_connections << "] has a size of " << this->m_output_connections.size() << std::endl;
}

完成后,输出如下:

ClLSTMNeuron::ClLSTMNeuron() [0000024BBC5720B8]: my OC [0000024BBC5720D0] has a size of 0
ClLSTMNeuron::ClLSTMNeuron() [0000024BBC5721D0]: my OC [0000024BBC5721E8] has a size of 0
ClLSTMNeuron::ClLSTMNeuron() [0000024BBC5722E8]: my OC [0000024BBC572300] has a size of 0
ClLSTMNeuron::ClLSTMNeuron() [0000024BBC572400]: my OC [0000024BBC572418] has a size of 0
ClLSTMNeuron::ClLSTMNeuron() [0000024BBC572518]: my OC [0000024BBC572530] has a size of 0
ClLSTMNeuron::ClLSTMNeuron() [0000024BBC572630]: my OC [0000024BBC572648] has a size of 0
ClLSTMNeuron::ClLSTMNeuron() [0000024BBC572748]: my OC [0000024BBC572760] has a size of 0
ClLSTMNeuron::ClLSTMNeuron() [0000024BBC572860]: my OC [0000024BBC572878] has a size of 0

在此输出中,我们可以清楚地看到每个 ClLSTMNeuron 实例的成员 m_output_connections 的地址。
但是,由于未知原因,当我使用这样的动态分配实例化它们时:

this->m_neurons = new ClLSTMNeuron[p_number_of_neurons]();
if (this->m_neurons == NULL)
{
    std::cout << "[Fatal Error] ClLSTMNeuronLayer::Init : Impossible to allocate " << p_number_of_neurons << " neurons in memory" << std::endl;
    return false;
}


for (size_t i = 0; i < p_number_of_neurons; i++)
{
    std::cout << "LSTM Neuron " << i << " is at [" << &this->m_neurons[i] << "] and it's OC [" << &this->m_neurons[i].m_output_connections << "] has size of " << this->m_neurons[i].m_output_connections.size() << std::endl;
}

我得到以下输出:

LSTM Neuron 0 is at [0000024BBC5720B8] and it's OC [0000024BBC5720D0] h size of 0
LSTM Neuron 1 is at [0000024BBC572150] and it's OC [0000024BBC572168] h size of 18446743758171348710
LSTM Neuron 2 is at [0000024BBC5721E8] and it's OC [0000024BBC572200] h size of 18446743758171348500
LSTM Neuron 3 is at [0000024BBC572280] and it's OC [0000024BBC572298] h size of 315538203026
LSTM Neuron 4 is at [0000024BBC572318] and it's OC [0000024BBC572330] h size of 17994617993471572384
LSTM Neuron 5 is at [0000024BBC5723B0] and it's OC [0000024BBC5723C8] h size of 0
LSTM Neuron 6 is at [0000024BBC572448] and it's OC [0000024BBC572460] h size of 17994617993471572409
LSTM Neuron 7 is at [0000024BBC5724E0] and it's OC [0000024BBC5724F8] h size of 0   

我们可以清楚地看到神经元本身的地址和成员m_output_connections的地址发生了变化:为什么会发生这种情况? 有没有继承/多态的概念不知道会不会?

我正在考虑典型的指针问题:索引不匹配、未初始化的指针等... 但似乎找不到任何会触发这种行为的东西。

问题在 Visual Studio 2015 和 GCC Linux 中是可复制的。

P.S 您可能会在这段代码中看到一些错误,因为它不遵循 non-written 规则,例如: 始终使用虚拟析构函数或类似概念。 请随时让我知道我可能犯的任何错误。

再次感谢您的宝贵时间!

如果我明白发生了什么事和你问什么,地址改变的原因是:

(14.3) — new T[5] results in a call of operator new[](sizeof(T)*5+x)

其中 x 是数组初始化开销

所以 new ClLSTMNeuron[p_number_of_neurons](); 并没有在内存中的连续区域中创建 p_number_of_neurons,它只是保留足够的内存来容纳 p_number_of_neurons 的 ClLSTMNeuron。

我不是 c++ 标准专家,但据我了解,这个过程类似于 new[] news up some memory。然后它调用 ClLSTMNeuron 的构造函数,该构造函数分配并初始化 ClLSTMNeuron 的内存和 returns 对该内存位置的 const 引用。然后将该内存移动到由 new.

分配的内存中

或更简单地说,构造函数设置一些内存,然后将其移入数组。所以这个的内存地址可能会变,也可能不会变。

同样,我不是专家,其他人可能会更恰当地解释它。如果您想做一些研究,请尝试阅读 c++ standard.

的第 5.3.4 节

如果你有:

ClNeuron* m_neurons

然后,一旦您对其进行迭代,以下内容就会出现问题:

this->m_neurons = new ClLSTMNeuron[p_number_of_neurons]();

因为指针算法将考虑 ClNeuron 而不是 ClLSTMNeuron 进行偏移计算。

所以m_neurons[n]

*static_cast<ClNeuron*>(static_cast<char*>(m_neurons) + n * sizeof(ClNeuron))

并不期望

*static_cast<ClLSTMNeuron*>(static_cast<char*>(m_neurons) + n * sizeof(ClLSTMNeuron))