【问题标题】:Beginner C++ programmer, confused over dynamic arrays初学者 C++ 程序员,对动态数组感到困惑
【发布时间】:2014-11-21 17:03:48
【问题描述】:

我正在尝试创建一个类似于 C++ 中内置矢量类的类。我已尝试按照 Walter Savitche 教科书中的所有说明进行操作,但无法使其正常工作。

代码是使用 Code::Blocks IDE 编写并使用 gcc 编译器编译的。

我认为我缺少的是数组参数和指向数组的指针之间的关系。 这就是我对普通变量的理解:

int *p1, *p2, *p3, *p4, a;
a = 5; // variable  of type int with value 5
p1 = &a; // p1 now points to the value 5
p2 = p1; // p2 now also points to the value of a
p3 = new int; // p3 points to an anonamous variable of type int with undefined value
*p3 = *p1 // value of variable changed to the value of a, namely 5, but doesn't point to a
p4 = new int; // p4 points to an anonamous variable of type int with undefined value
*p4 = 5; // value of variable changed to  5
p4 = p1 // p4 now also points to the value of a

这是我对数组和指向数组的指针基本上不了解的地方

int *p1, *p2, *p3, *p4, a[3] = {4, 5, 6}; // a points to the first indexed element of the array, namely 4
p1 = a; // p1 points to the exactly the same thing as a
p2 = new int[3]; // p2 points to an array of base type int with undefined values
p2[0] = 8; // is this the correct usage? is p2 "dereferenced" 
p2[1] = 9;
p2[2] = 10;
p2[2] = p1[2]; // again is this correct? is the third element of the array pointed to by p2 now equal to 6?
*p3 = a // what does this mean?
p4 = new int[4]; // p4 points to an array of base type int with undefined values
p4[0] = p2[0]; 
p4[1] = p2[1];
p4[2] = p2[2];
p4[3] = 3
p2 = p4 // p2 now points to p4, but what happens to the array p2 was pointing to?
delete [] p2; // does this destroy the pointer and the array it is pointing to or just one or the other?

为了完整起见,我的班级定义如下:

class VectorDouble
{
public:
    // constructors
    VectorDouble(); // default constructor
    VectorDouble(int init_count); // user specified
    VectorDouble(const VectorDouble& vd_object); // copy constructor
    // destructor
    ~VectorDouble();
    // accessors
    int capacity_vd(); // get max_count
    int size_vd(); // get amt_count
    double value_at(int index); // get value of "value" at index i
    // mutators
    void push_back_vd(double put_at_end); // insert new element at end of "value"
    void reserve_vd(int incr_capacity); // set max_count
    void resize_vd(int incr_size); // set amt_count
    void change_value_at(double d, int index); // set value of "value" at index i
    // overloaded =
    void operator =(const VectorDouble& vd_object_rhs);
    // other
    friend bool operator ==(VectorDouble vd_object1, VectorDouble vd_object2);
private:
    double *value; // pointer that points to array of type double
    int max_count; // the memory allocated to the array
    int amt_count; // the amount of memory in use
};

而麻烦的功能是:

void VectorDouble::push_back_vd(double put_at_end)
{
    double *temp;
    if(amt_count == max_count)
        max_count += 1;
    temp = new double[max_count];
    for(int i = 0; i < amt_count; i++)
        temp[i] = value[i];
    amt_count += 1;
    temp[amt_count] = put_at_end;
    value = temp;
}

成员函数似乎只是插入 0.0 而不是用户输入,我不知道为什么......

主要:

VectorDouble vec1(10);
    double dd;

    cout << "Enter 3 doubles to vec1:\n";
    for(int i = 0; i < 3; i++)
    {
        cout << i << ": ";
        cin >> dd;
        vec1.push_back_vd(dd);
    }

    cout << "The variables you entered were:\n";
    for(int i = 0; i < 3; i++)
        cout << i << ": " << vec1.value_at(i) << endl;

我输入:

12.5 16.8 15.2

我回来了:

0 0 0

我修好了!唯一的问题是错误非常简单。很抱歉浪费大家的时间,但感谢大家,我确实学到了很多!

错误是我放置了amt_count += 1;,我习惯于从1 而非0 开始索引的数组(我已经用R 语言编写了很多代码)。解决了内存泄漏的更正代码是:

void VectorDouble::push_back_vd(double put_at_end)
{
    double *temp;
    if(amt_count == max_count)
        max_count += 1;
    temp = new double[max_count];
    for(int i = 0; i < amt_count; i++)
        temp[i] = value[i];
    temp[amt_count] = put_at_end;
    amt_count += 1;
    delete [] value;
    value = temp;
}

【问题讨论】:

  • 为什么你认为你“不能让它工作”?出了什么问题?
  • 指针从不指向。它指向一个object(它有一个值或者是不确定的,但没关系),在一个对象后面,无处(nullptr),或者像任何其他未初始化的对象一样是不确定的。
  • 你的函数push_back_vd确实在泄漏内存,你需要删除之前分配的空间。所以你需要在调用value = temp之前调用delete[] value;(当然是在你复制旧值之后)
  • "匿名变量" - 我假设您的意思是匿名的。 C++ 没有这样的东西。你创建了一个堆变量
  • 如果我使用delete [] value;,这会不会搞砸一切,因为我实际上将删除一个私有成员变量?还是这个动作只发生在成员函数的范围内?

标签: c++ arrays class pointers vector


【解决方案1】:

这就是我对普通变量的理解

所有正确,但需要注意的是我会避免使用术语“指向值 x”;你指向的是 object,它的值又是 x

这是我对数组和指向数组的指针本质上不理解的地方

您将指针与数组混淆了。在int a[3] 中,a 是一个数组。它不是指针。它是一个数组。

*p3 = a 无效,因此没有任何意义。

p2 现在指向 p4,但是 p2 指向的数组会发生什么?

你已经泄露了它。

// 这会破坏指针和它指向的数组还是仅破坏其中一个?

它破坏了你new'd,指针指向的东西。即数组

否则全部正确。


至于你的向量实现,主要问题是temp[amt_count] 是一个溢出,因为你已经增加了amt_count。此外,向量实现通常呈指数增长,而不是按需增长。最后,你泄露了之前的存储空间。

【讨论】:

  • 见我上面的评论。不是“全部正确”。 (更好,虽然我认为不够强大。这是不同概念的基本混淆。)
  • @Deduplicator:我在这个答案中没有提到你评论的哪一部分?
  • (在您更正之后)我只是认为(只是)明显错误的术语与暗示对所涉及概念的基本误解的术语之间存在关键区别,并要求清楚地解释概念。
  • @Deduplicator:什么更正?我刚刚添加了有关向量实现的信息。你从来没有写过关于向量实现的评论。你很奇怪:/
  • 您更正了第一点,它只是“全部正确”。在我的第一条评论之前。我只是不认为你在那里走得足够远。而你,我很奇怪。
【解决方案2】:

使用不同的术语可能会对您有所帮助:

指针只是一个普通的变量。它不保存整数、浮点数、双精度等,而是保存内存地址。考虑以下几点:

int* p = nullptr; // p has the value "nullptr" or null memory address
int i = 5;        // i has value 5

p = &i;           // p now has the value of the address of i

& 符号获取变量的地址

一个星号取消引用一个指针;也就是说,它将获取存储在指针所持有的内存地址中的值:

cout << *p << endl; // Prints whatever is stored in the memory address of i; 5

至于您的矢量实现,请尝试将此行 amt_count += 1; 移至此行下方: temp[amt_count] = put_at_end;,因为您正在尝试访问数组末尾之外的内容。


你的大部分理解是正确的。但是...

a[3] = {4, 5, 6}; // a points to the first indexed element of the array, namely 4

虽然数组和指针可以以类似的方式进行索引和处理,但它们是不同的,它们的差异会导致一些偷偷摸摸的错误;所以要小心这个声明。

*p3 = a // what does this mean?

这是无效的。您的类型不匹配:*p3 是整数,a 是数组。

p2 = p4 // p2 now points to p4, but what happens to the array p2 was pointing to?

p2 指向的数组现在是内存泄漏。这很糟糕。

delete [] p2; // does this destroy the pointer and the array it is pointing to or just one or the other?

指针的值不变。但是,它指向的内存已被释放,因此取消引用它会给您带来未定义的结果。删除后最好设置p2 = nullptr;

This answer 可能有助于您理解数组和访问它们的元素。

【讨论】:

  • 您错过了一些问题(并建议它们不是问题),但除此之外,这是一个很好的答案。
【解决方案3】:

“p2 现在指向 p4,但是 p2 指向的数组会发生什么?”

它被“泄露”了,这意味着它仍然被分配,但没有办法再访问它了。如果你继续这样做是同一个程序,你的内存大小会不断增长

其他语言(Java、c#、...)具有“垃圾收集器”,可检测何时发生这种情况并自动释放内存

这个问题的 C++ 解决方案是永远不要使用裸数组和指针。相反,您使用 std::vector 和 std::shared_ptr;这些将为您清理

【讨论】:

  • C++ 标准对运行 GC 提供了基本支持(严格的指针派生和那些用于注册附加根等的 GC 宏)。它只是很少使用。见[util.dynamic.safety]
【解决方案4】:

你的函数真的错了……

void VectorDouble::push_back_vd(double put_at_end)
{
    double *temp;
    if(amt_count == max_count)
        max_count += 1;
    temp = new double[max_count];
    for(int i = 0; i < amt_count; i++)
        temp[i] = value[i];
    amt_count += 1;
    temp[amt_count] = put_at_end;
    value = temp;
}

请注意,在每次调用中,您都会分配新数组(即使还有空间),复制其中的所有内容并泄漏内存(旧数组)...

这里是稍微修正的版本,但不保证完全没问题(;

void VectorDouble::push_back_vd(double put_at_end)
{
    if(amt_count == max_count)
    {
        max_count += 1;
        double *temp = new double[max_count];
        for(int i = 0; i < amt_count; i++)
            temp[i] = value[i];
        delete[] value;
        value = temp;
    }
    value[amt_count] = put_at_end;
    amt_count += 1;
}

【讨论】:

  • 如果您同时没有删除元素,这仍然会在每次调用时分配。通常,向量实现呈指数增长。 value[amt_count] 也是溢出。
  • @LightnessRacesinOrbit - 嗯,这只是作者所拥有的稍微更正的版本(;我不会做他的全部作业(;分配不是溢出 - 分配的内存块有 @ 987654324@ 元素使用amt_count 的初始值,所以对于新元素来说正好是“一个更多”。这也正是作者所拥有的——我只修复了泄漏部分和无用的分配,如果还有空间。
  • " 分配的内存块有 amt_count + 1 个元素,使用 amt_count 的初始值" 那是什么初始值?他没有显示一个,0 是唯一有意义的初始值。 (如果它按照您的建议从1 开始,那么现在您的for 循环是错误的,因为它不会复制所有数据。)从0 开始,您的代码和OP 的代码每次都会溢出。
  • @LightnessRacesinOrbit - 好的,我现在看到了问题,我会稍微编辑一下答案。 ...完成了,我只是把amt_count的增量放在最后,我想现在没问题了(;
猜你喜欢
  • 2020-03-29
  • 2013-12-31
  • 1970-01-01
  • 1970-01-01
  • 2011-04-05
  • 1970-01-01
  • 2015-07-30
  • 2015-12-01
  • 1970-01-01
相关资源
最近更新 更多