简体   繁体   English

从pop_back()之后无法访问向量(分段错误)

[英]Cannot access vector (segmentation fault) after pop_back() from it

I am writing a simple in-memory database. 我正在编写一个简单的内存数据库。 It should support transaction , meaning that BEGIN command creates a new block, the operations in which can be canceled by ROLLBACK command. 它应该支持transaction ,这意味着BEGIN命令创建一个新块,可以通过ROLLBACK命令取消其中的操作。

I am implementing the list of transaction block s using a vector . 我正在使用vector实现事务block的列表。 In case of BEGIN a new block is created by push_back() ing an unordered_set of all keys in that transaction block. 如果是BEGIN则通过push_back()对该事务块中所有键的unordered_set创建一个新块。 This is defined as vector<unordered_set<string>> key_in_blocks 定义为vector<unordered_set<string>> key_in_blocks

What follows is an example of the commands. 以下是命令的示例。 SET sets value to a variable, GET gets the value of the variable. SET将值设置为变量, GET获取变量的值。

BEGIN
SET a 10
GET a       <-- this prints 10
BEGIN
SET a 20
GET a       <-- this prints 20
ROLLBACK
GET a       <-- this prints 10
ROLLBACK    <-- Segmentation Fault here!

So there is a default block at first, and keys_in_block would look like [()] , here () is denoting set , [] denotes vector . 因此,首先有一个默认block ,keys_in_block看起来像[()] ,这里()表示set ,[]表示vector

When BEGIN is issued, a new block is created, and SET adds a key a to the block, so keys_in_block = [(), ('a')] . 发出BEGIN ,将创建一个新块,然后SET将一个密钥a添加到该块,因此keys_in_block = [(), ('a')]

The next BEGIN & SET part is similar, making keys_in_block look like [(), ('a'), ('a')] . 下一个BEGINSET部分相似,使keys_in_block看起来像[(), ('a'), ('a')]

Now ROLLBACK undoes operations done in the last block, and keys_in_block should be [(), ('a')] , because it is pop_back() ed. 现在, ROLLBACK撤消在最后一个块中完成的操作,并且keys_in_block应该为[(), ('a')] ,因为它是pop_back()

I think my program does what it needs to do up to this point. 我认为我的程序可以做到这一点。

On the second ROLLBACK , it throws segmentation fault, and it looks like I cannot even access the keys_in_block variable at all. 在第二个ROLLBACK ,它引发分段错误,并且看起来我什至keys_in_block无法访问keys_in_block变量。

Below is my code snippet. 以下是我的代码段。 It runs inside an infinite while loop, taking commands from user. 它在无限while循环内运行,接受用户的命令。

} else if (command == "BEGIN") {
    int num_blocks = keys_in_block.size();
    keys_in_block.resize(num_blocks + 1); //new transaction block
} else if (command == "ROLLBACK") {
    if (keys_in_block.size() <= 1) {
        cout << "NO TRANSACTION" << endl;
    }
    else {
        auto &recent_keys = keys_in_block.back();
        // undo operations for keys in the most recent transaction block
        for (const string &key : recent_keys) {
            //...
            //...    a bunch of operations that undoes what's done
            //...    in the most recent transaction block.
            recent_keys.erase(key); // erase all keys in the last entry in `keys_in_block`.
        }
        auto & aa = keys_in_block; //***Just for testing, not relevant to the program.
                                   //This throws Segmentation Fault on second ROLLBACK.
        keys_in_block.pop_back();  //Pop back the last transaction block.
    }
}

In the code snippet, I marked where the segmentation fault is thrown using //*** . 在代码段中,我使用//***标记了引发分段错误的位置。

I added that line to because keys_in_block.pop_back() threw segmentation fault, and wanted to see if it throws seg fault just by accessing it. 我添加该行是因为keys_in_block.pop_back()引发了分段错误,并且想通过访问它来查看是否引发seg错误。

To me, the algorithm looked absolutely correct, and I couldn't find out what the cause of the problem was. 在我看来,该算法看起来绝对正确,而且我无法找出问题的原因。

ROLLBACK doesn't execute when keys_in_block has one block or less, so pop_back() cannot be an issue. 如果keys_in_block小于或等于一个block ,则ROLLBACK不执行,因此pop_back()不会成为问题。

If you need the code for the SET command part, let me know, but I don't think there is a problem in that code. 如果您需要SET命令部分的代码,请告诉我,但我认为该代码没有问题。

I commented out recent_keys.erase(key); 我注释掉了last_keys.erase(key); because that wasn't necessary, and it worked!! 因为那不是必须的,而且有效!! and yet, I cannot figure out why. 但是,我不知道为什么。

This is the reason that your program crashes, since you are deleting the element while iterating the container at the same time. 这是您的程序崩溃的原因,因为您要在同时迭代容器的同时删除元素。 The for (const string &key : recent_keys) loop is trying to running from the begin to the end, however you remove the element in the loop, so that the iterator goes invalid, so the loop can't continue and crashes when it tries to increment the iterator. for (const string &key : recent_keys)循环尝试for (const string &key : recent_keys)运行,但是您删除了循环中的元素,以使迭代器无效,因此循环无法继续并在尝试执行时崩溃增加迭代器。

This is weird because recent_keys is just a reference to the last element in the vector, and I was removing elements in that last element in the vector using erase. 这很奇怪,因为last_keys只是对向量中最后一个元素的引用,而我正在使用擦除删除向量中最后一个元素中的元素。 Can you explain what's happening, and how I could avoid making the same mistake? 您能解释发生了什么,如何避免犯同样的错误?

Yes recent_keys is a reference to the last element of the vector, but you should not remove element while iterating it. 是的, recent_keys引用是对向量的最后一个元素的引用,但是在迭代它时不应删除该元素。 If you want to avoid making the same mistake then never do anything like this again. 如果您想避免犯同样的错误,那就再也不要做这样的事情。

In your example you can just iterate the recent_keys , then let keys_in_block.pop_back() do the clean up. 在您的示例中,您可以仅迭代recent_keys ,然后让keys_in_block.pop_back()进行清理。 For other cases, you might want to iterate the container and collect element references, then remove those elements after done iterating the container, you can use vector 's erase combined with std::remove_if to accomplish such thing. 对于其他情况,您可能要迭代容器并收集元素引用,然后在完成容器迭代后删除那些元素,可以将vectorerasestd::remove_if结合使用来完成此操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM