【问题标题】:Auto initialized objects in two-dimensional array?二维数组中的自动初始化对象?
【发布时间】:2017-07-23 13:44:26
【问题描述】:

当我寻找内存泄漏时,我发现了这个有趣的特性。 我有

class Perseptron :
public Neiron
{
private:
 Neiron** neirons;
 }

在类的头文件中。 当neirons[i][0] 初始化时,我在调试器中看到neirons[i][1,2...n] 字段在构造函数neirons[i][1,2...n] 初始化之前已经初始化了neirons[i][0] 字段值等值。

neirons = new Neiron*[layerCount];
for (int i=0;i<layerCount;++i)
{
    neirons[i] = new Neiron[this->eachLayerCount[i]];
    for (int j=0;j<this->eachLayerCount[i];++j)
    {
        if (i == 0) {
            neirons[i][j] = Neiron(2, this->inCount, this->eachLayerCount[i + 1], i, j);
            neirons[i][j].inX = this->inX;
        }
        else if(i!=layerCount-1) 
            neirons[i][j] = Neiron(2, this->eachLayerCount[i - 1], this->eachLayerCount[i + 1],i,j);
        else 
            neirons[i][j] = Neiron(2, this->eachLayerCount[i - 1], 1,i,j);
    } 
}

我的Neiron 构造函数:

Neiron::Neiron(int limit,int inCount,int outCount,int layerN,int neironN)

Neiron::Neiron(){}

这是为什么呢?

编辑

MCVE

class Test{
public:
int fieldA;
Test(int a)
{
    fieldA = a;//when a=3, why already fieldA=2 ?
}
Test()
{

}
 };

int main()
{
int layers[] = { 3,4,2 };
int counter = 0;
Test** test=new Test*[3];
for (int i = 0;i < 3;++i)
{
    test[i] = new Test[layers[i]];
    for (int j = 0;j < layers[i];++j)
    {
        test[i][j] = Test(counter);
        counter++;
    }
}
for (int i = 0;i < 3;++i) delete[] test[i];
delete[] test;
return 0;
}

【问题讨论】:

  • 为什么不使用std::vector
  • @NathanOliver,对我来说,动态内存的工作非常有趣......
  • 您是否还在此for 循环之前创建了指针数组?如果不是,那么您可能正在访问触发未定义行为的未分配内存。在创建neirons 之前,您不能分配给neirons[i]
  • @Kos,我刚刚编辑了问题

标签: c++ arrays pointers object dynamic


【解决方案1】:

使用new 创建对象总是隐式调用构造函数,这是标准的一部分,这种方法非常方便。所以写neirons[i] = new Neiron[this-&gt;eachLayerCount[i]]; 的意思是“用Neiron 类的N 个对象创建数组并将指向该数组的指针存储到neirons[i]”。结果,分配了动态缓冲区,在其中创建了N个对象(调用了构造函数)。

当前案例与创建单个对象 neirons[i] = new Neiron; 没有太大区别,您可能会习惯于隐式调用构造函数。

C++标准相关部分:

调用默认构造函数...创建动态类对象 存储持续时间 (3.7.4) 由 new-expression 创建,其中省略了 new-initializer (5.3.4) ...

编辑

我可能误解了这个问题。看起来您想知道某些字段在仅调用默认构造函数时正在初始化,这对它们没有任何作用。不应该有魔法,未初始化的变量从动态内存中留下的垃圾中获取其值。如果你看到一些有意义的值而不是随机的东西或零,你可能已经把你的新对象放入了内存,它刚刚被释放用于相同的目的。如果您对此解释不服,请创建MCVE,以便我们逐步重现和解释场景。

编辑 2

感谢澄清,我现在看到了问题。以下是 MCVE 内部的情况:

test[i] = new Test[layers[i]];

动态缓冲区被分配并填充layers[i] 对象。对象是使用默认构造函数创建的,因此它们的字段没有被初始化,但包含留在堆上的垃圾(例如,这些对象的 fieldA 设置为 -842150451 或任何其他无意义的值)。

for (int j = 0;j < layers[i];++j)
{
    test[i][j] = Test(counter);
    counter++;
}

此示例在右侧使用匿名对象,因此实际上与此等效:

for (int j = 0;j < layers[i];++j)
{
    Test temporaryObject(counter);
    test[i][j] = temporaryObject;
    counter++;
}

程序使用参数化构造函数在堆栈上创建临时对象,然后用它初始化 test[i][j],销毁临时对象并为下一个 i/j 重复它。

第一次迭代 (i=0, j=0) 在堆栈中分配未初始化的缓冲区来存储临时对象并为其调用参数化构造函数,因此您可以看到构造函数中的 fieldA 从垃圾变为零。然后使用临时对象初始化test[i][j]并销毁,释放内存。

第二次迭代 (i=0, j=1) 分配完全相同的内存区域来存储临时对象(您可以通过检查 &amp;temporaryObject 或在参数化构造函数中检查 this 来验证它)。由于此内存包含以前用作临时对象的剩余部分,因此您会看到构造函数将 fieldA 从 0(上次迭代的左侧)更改为 1。依此类推。

我想强调,上述过程仅与临时对象有关。 test[i][j] 只初始化两次:

  1. 在使用test[i] = new Test[layers[i]]; 创建期间带有随机垃圾
  2. 来自带有test[i][j] = Test(counter); 的临时对象的实际值

【讨论】:

  • 我在我的问题中添加了 MCVE。
  • 不错!但是,无法在本地复制它。使用 VS2015,我总是会得到一些奇怪的 fieldA 初始值,例如 -842150451(即 0xCDCDCDCD),直到初始化行 test[i][j] = Test(counter); 被命中。你用什么工具链?
  • VS2015 Update 2. 在第一次初始化之前它是-858993460。那么它等于a-1....
  • 因此您会看到以下内容:一旦test[0][0] = Test(0); 被执行,所有test[0][x] 的fieldA 都会设置为零。一旦test[0][1] = Test(1); 被执行,所有test[0][x](对于x>=1)都将fieldA 设置为1,依此类推,对吗?在test[i][j] = Test(counter); 之前使用std::cout &lt;&lt; "(" &lt;&lt; i &lt;&lt; ", " &lt;&lt; j &lt;&lt; "): " &lt;&lt; test[i][j].fieldA &lt;&lt; "\n"; 的控制台输出是否显示相同的内容?
  • cout &lt;&lt; test[i][j].fieldA &lt;&lt; endl; 显示 '-84215045' 九次。但在下面的情况下,我得到 -858993460 0 1 2 3 4 5 6 7。Test(int a) { // cout &lt;&lt; fieldA &lt;&lt; endl; fieldA = a; }
猜你喜欢
  • 2021-02-22
  • 1970-01-01
  • 1970-01-01
  • 2012-01-27
  • 1970-01-01
  • 2014-02-18
  • 2012-11-29
相关资源
最近更新 更多