【问题标题】:Removing elements from dynamic arrays without changing the index order从动态数组中删除元素而不更改索引顺序
【发布时间】:2011-09-05 18:59:37
【问题描述】:

我一直在尝试在不更改索引顺序的情况下从数组中擦除一个元素,例如:

class MyObject  
{  
    int id;  
public:  
    MyObject() { }  
    void setId( int i ) { id = i; }  
    void showId() { std::cout << "ID: "<< id << "\n"; }
};  
MyObject *myArray;

int main ( )  
{  
    myArray = new myArray[6];  
    for( int i = 0; i < 6; i++ )  
    {  
        myArray[i]->setId(i); 
        myArray[i]->showId();
    }  
}  

我想删除myArray[3] 而不更改其他索引。例如:

myArray[0] = ID: 1  
myArray[1] = ID: 2  
myArray[2] = nothing  
myArray[3] = ID: 4  
myArray[4] = ID: 5  
myArray[5] = ID: 6    

我尝试过使用memset(),但没用。

memset(&myArray[3],0,sizeof(MyObject));

【问题讨论】:

  • 这个的最终目标是什么?这似乎有点反模式。也许有更好的方法来完成最终目标。
  • 离题,但请记住数组索引从零开始。 myArray[6] 越界。
  • @Mike 可能不是题外话,因为我们所知道的故障模式是“它不起作用”。
  • 这是 C++,所以首先不要使用数组 - 使用 std::vector&lt;MyObject*&gt; 代替。如果您需要能够有效地删除中间的元素并使现有引用(迭代器)保持有效,请使用std::list&lt;MyObject*&gt;
  • 基于问题和 cmets,我建议您考虑移动到无序(基于哈希)地图,除非您有需要矢量的特定原因(通常,这将是一个要求内存是连续的)。 boost::unordered_map 将是一个很好的起点。

标签: c++ arrays indexing


【解决方案1】:

在 C++ 语言中没有“无”之类的东西。一旦你有了一个数组,该数组的所有元素都将包含“某物”。没有办法让数组元素消失,同时将所有其他元素保留在它们原来的位置。你不能在数组中创建一个洞。

在这种情况下,您所能做的只是将某个元素标记为“已删除”,然后再将其识别为“已删除”。当然,该元素将继续物理存在。您必须将其识别为“已删除”并在您的进一步处理中忽略它。您可以在对象中添加一些bool is_deleted 字段,也可以使用id 的一些保留值(如-1)来指示已删除的元素。

在您使用memset 的示例中,您基本上将id 设置为零。 0 是有效的 id 值吗?如果不是,那么0 是标记已删除元素的不错选择。从这个意义上说,您的memset 尝试工作得很好,因为它应该。虽然我建议通过显式将零分配给id 来做到这一点,而不使用memset

【讨论】:

    【解决方案2】:

    您正在调用memset 在对象实例的顶部写入一堆零。不要这样做!如果您的课程是真正的 POD 课程,您可能会侥幸逃脱。您最终可能只是将 ID 设置为 0。但您的课程可能还有更多您没有显示的内容。无论如何,即使不是POD,也不要那样使用memset

    您可以存储指向对象的指针并使用空指针来指示那里没有任何内容。我会用std::vector&lt;MyObject*&gt; 来做这件事。或者您使用哨兵对象实例,例如 ID 为 -1。

    另一件可能存在问题的事情是您似乎在使用基于 1 的索引。 C++ 数组是从 0 开始的,所以第一个元素是 myArray[0] 而不是 myArray[1]

    【讨论】:

    • 我认为通过“不更改索引顺序”,他希望对象的 id 与其所在的索引相对应。但可能是错误的。
    • @Seth 你可能是对的。我认为使用基于 1 的索引可能是一个更大的问题。
    • 对主帖中的错误输入感到抱歉>
    • 主要问题是你没有说你的问题是什么,我们都不得不猜测。如果你想要一个空值,就像我说的那样,使用指针。
    【解决方案3】:

    这样使用memset就是将该对象的所有字节设置为0。这通常相当于将id设置为0,因为对象的内存是其成员的内存(不包括vtables,padding , ETC)。但无论如何都不要这样做。

    一种方法是使用new 并拥有一个指针数组。

    MyObject* myArray[6];
    
    int main ( )  
    { 
        for( int i = 0; i < 6; i++ )  
        {
            myArray[i] = new MyObject;
            myArray[i]->setId(i); 
            myArray[i]->showId();
        }  
    }
    

    然后全部显示:

    for (int i = 0; i < 6; i++) {
        cout << "myArray[" << i << "] = ";
        if (myArray[i])
            myArray[i]->showId();
        else
            cout << "nothing" << endl;
    }
    

    然后当你想删除一个对象时,delete它并将它的指针设置为NULL

    delete myArray
    myArray[3] = NULL;
    

    那么当你对myArray 中的一个对象做任何事情时,你必须检查它是否是NULL 以查看它是否是一个有效的对象。

    【讨论】:

    • 但是我怎么能用动态数组做到这一点呢?
    • @k3oy 将MyObject* myArray[6] 更改为MyObject** myArray = new MyObject*[6];
    【解决方案4】:

    考虑boost::optional

    typedef boost::optional<MyObject> MyObjectOpt;
    MyObjectOptArr *myArray;
    

    语法/用法有点不同(类似于使用指针):

    for (int i = 0; i < 6; ++i) {      
        if (myArray[i])
            cout << "myArray[" << i << "] = " << *(myArray[N]);
        else
            cout << "nothing" << endl;
    }
    

    要取消设置值:

    myArray[N] = boost::none;
    

    【讨论】:

    • 那么,当我从 myArray 中删除任何值时,其他值仍会位于同一索引处?
    • 删除索引分配 boost:none - 我会更新答案
    猜你喜欢
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 2021-07-20
    • 1970-01-01
    相关资源
    最近更新 更多