【问题标题】:Return struct created from new struct within function返回从函数内的新结构创建的结构
【发布时间】:2021-06-12 13:19:44
【问题描述】:

在这段代码中,函数返回的结构中的数组指针是否指向用新结构定义的同一块内存?

#include <iostream>
#include <math.h>

struct Arr
{
    const int Col;
    const int Row;
    double* CAR{ new double[Col * Row] };
    Arr(int y, int x) : Col(x), Row(y) {}
    void Del() { delete[] CAR; }
    int Indx(int y, int x) { return (x + y * Col); }
    int Size() { return Col * Row; }
    void Print(std::string title);
};
void Arr::Print(std::string title)
{
    std::cout << title << '\n';
    for (int I = 0; I < Row; I++)
    {
        for (int In = 0; In < Col; In++)
        {
            std::cout << CAR[Indx(I, In)] << " / ";
        }
        std::cout << '\n';
    }
}
const Arr retfunc(std::string h, Arr& a, Arr& b)
{
    Arr* temp = NULL;
    if (h == "Had") 
    {
        temp = new Arr(a.Row, a.Col);
        for (int I = 0; I < a.Row; I++)
        {
            for (int In = 0; In < a.Col; In++)
            {
                temp->CAR[temp->Indx(I, In)] = a.CAR[a.Indx(I, In)] * b.CAR[b.Indx(I, In)];
            }
        }
    } Arr T = *temp; return T;
}

int main()
{
    int val = 5;
    Arr a(2, 2);
    Arr b(2, 2);
    for (int I = 0; I < 2; I++)
    {
        for (int In = 0; In < 2; In++)
        {
            a.CAR[a.Indx(I, In)] = 10.0 / val + In;
            b.CAR[b.Indx(I, In)] = 8.0 / val + In;
        }
    }
    a.Print("a");
    b.Print("b");
    Arr S = retfunc("Had", a, b);
    S.Print("S");   
    S.Del();
}

那么本质上,在 S 上调用 delete 是否会清除在 retfunc 中分配的相同内存?

【问题讨论】:

  • 当您执行double* CAR{ new double[Col * Row] }; 时,ColRow 的值是多少?提示:它们的值尚未设置...
  • 你应该在构造函数中实现new
  • @Damien 它们在构造函数中设置,但为时已晚!我不这么认为(但不太确定)。因此,我在 g++ 10.2 中进行了尝试:Demo in coliru。据此,在构造函数中覆盖默认初始化的成员似乎是有效的,并且它们仍然会在其他成员的默认初始化中被考虑。 (我承认演示还不是证明......)
  • @Someprogrammerdude:我在 MSVC 和调试模式下测试了该代码,并惊讶于 MSVC 正确处理它:它按声明顺序分配成员,因此 ColRow 确实在之前初始化用于CAR 初始化。该代码仍然是内存泄漏的秘诀,但初始化似乎至少对于 MSVC 是正确的(未使用其他编译器测试)
  • @Scheff 确实很有趣。从律师的角度来看,我可能是错的。但就个人而言,我会避免这样的建设...... :)

标签: c++ pointers struct new-operator


【解决方案1】:

您应该在 retfunc 的末尾有一个 return *temp,它被声明为返回一个 Arr 对象。修复此问题后,S.Del() 会正确释放在retfunc 中分配的内存块。

然而,这是一个糟糕的代码。您未能释放在ab 对象中分配的块。不是错误,因为程序结束会释放它们,但这是导致内存泄漏的原因。此外,您在构造函数中有一个手动分配的内存块,但没有显式的复制构造函数或赋值运算符。如果你写:

Arr c = a;

c 和 a 都将共享同一个 CAR 数组,这可能是也可能不是您想要的。

长话短说,我认为这不是一个明显的错误,但至少是一种反模式。

【讨论】:

    【解决方案2】:

    对于初学者来说,函数 retfunc 存在内存泄漏,因为在函数内动态分配的对象 temp 未被释放。

    由于该类没有显式的复制构造函数,因此在此声明中

    Arr T = *temp
    

    使用成员方式将对象 temp 的数据成员复制到对象 T。对象 temp 的指针 CAR 的值将被复制到对象 T 的指针 CAR 中,并且由于是有效的指针到动态分配对象temp的生命周期。

    当一个类中有动态分配的内存时,您需要编写显式析构函数,为左值和右值复制构造函数,为左值和右值复制赋值运算符。

    【讨论】:

    • 这就是我的想法,但我在想可能是因为 CAR 是一个指针,当来自 temp 的指针被复制到 S 然后 S 到 T 时,这是否意味着它只是复制内存地址?
    • @Yugenswitch 正确,指针被复制。但是对象temp 本身呢?
    • 啊太棒了!所以如果我只是删除 temp 那么没有内存泄漏?
    • 如果我的测试是正确的,主要问题是在retfunc 末尾缺少return *temp(除了因为分配的块没有在 dtor 中释放而导致内存泄漏的代码之外,和5的规则)
    • 输入非常感谢!指针仍然很菜鸟。
    猜你喜欢
    • 2018-05-27
    • 1970-01-01
    • 2019-05-06
    • 2015-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多