【问题标题】:Calling clear() on a vector does not actually delete data in data()?在向量上调用 clear() 实际上不会删除 data() 中的数据?
【发布时间】:2019-06-08 00:07:11
【问题描述】:

总结

似乎只是在向量上调用clear() 不足以清除它。

vector_full_of_stuff.clear();

我必须先调用clear(),然后再调用shrink_to_fit() 才能完全删除其中的所有数据。

vector_full_of_stuff.clear();
// AND THEN  
vector_full_of_stuff.shrink_to_fit();

什么给了?这成为了一个问题,因为当我在向量上调用 data() 时,它会包含我认为应该在我之前在代码中调用 clear() 时清除的内容。

其他详情

我正在执行计算机网络任务,我必须将 PASV 命令结果解析为 IP 和端口号。在解析以逗号分隔的虚构 PASV 命令结果时,我注意到如果我解析一个三位数字,然后解析一个两位数字,我会在调用 data() 时从前一个解析中得到第三位数字,即使我不应该(?)因为我在它之前调用clear()

例如
PASV 命令结果 = 209,202,252,54,19,15
“252”中的“2”在解析时会延续到“19”中。

代码

// this one actually deletes data
void splitString(string str, char delimiter, vector<string> * out) {
    vector<char> word_buffer;
    for (int i = 0; i < str.length(); ++i) {
        if (str[i] == delimiter) {
            out->push_back(word_buffer.data());
            word_buffer.clear();
            word_buffer.shrink_to_fit();
        } else {
            word_buffer.push_back(str[i]);
        }
    }
    out->push_back(word_buffer.data());
    word_buffer.clear();
}
// 

// this one doesn't
// the only thing that's different about this one 
// is that its missing shrink_to_fit()
void splitString(string str, char delimiter, vector<string> * out) {
    vector<char> word_buffer;
    for (int i = 0; i < str.length(); ++i) {
        if (str[i] == delimiter) {
            out->push_back(word_buffer.data());
            word_buffer.clear();
            // word_buffer.shrink_to_fit(); // need this to delete data
        } else {
            word_buffer.push_back(str[i]);
        }
    }
    out->push_back(word_buffer.data());
    word_buffer.clear();
}
//

// main driver code
int main() {
    vector<string> user_input_tokens;
    string port = "209,202,252,54,19,15";
    splitString(port, ',', &user_input_tokens);
    for (string str : user_input_tokens) {
        cout << str << ".";
    }
}
//

预期输出:

209.202.252.54.19.15.

实际输出:

209.202.252.542.192.152.

【问题讨论】:

标签: c++ vector stl erase shrink


【解决方案1】:

向量的data() 方法返回一个原始指针,指向该向量在内存中分配的数组。如果需要,clear() 会破坏该数组的 内容,并将向量的 size() 设置为 0,但不会重新分配数组本身,因此不会更改向量的 capacity()。调用向量的 shrink_to_fit() 方法会重新分配数组,以便其 capacity() 匹配其 size(),如果可能的话(shrink_to_fit() 仅供参考,不保证实际执行任何操作)。

另外,当自己从char* 指针构造std::string 时,char 数据需要以空值结尾,但您的数据不是。在使用 data() 之前,您需要将空终止符推入向量中:

void splitString(const string &str, char delimiter, vector<string> * out) {
    vector<char> word_buffer;
    for (int i = 0; i < str.length(); ++i) {
        if (str[i] == delimiter) {
            word_buffer.push_back('\0');
            out->push_back(word_buffer.data());
            word_buffer.clear();
        } else {
            word_buffer.push_back(str[i]);
        }
    }
    if (!word_buffer.empty()) {
        word_buffer.push_back('\0')
        out->push_back(word_buffer.data());
    }
}

否则,您可以在构造字符串时简单地考虑向量的size(),不需要空终止符:

void splitString(const string &str, char delimiter, vector<string> * out) {
    vector<char> word_buffer;
    for (int i = 0; i < str.length(); ++i) {
        if (str[i] == delimiter) {
            out->push_back(string(word_buffer.data(), word_buffer.size()));
            // alternatively:
            // out->emplace_back(word_buffer.data(), word_buffer.size());
            word_buffer.clear();
        }
        else {
            word_buffer.push_back(str[i]);
        }
    }
    if (!word_buffer.empty()) {
        out->push_back(string(word_buffer.data(), word_buffer.size()));
        // alternatively:
        // out->emplace_back(word_buffer.data(), word_buffer.size());
    }
}

话虽如此,还有其他方法可以实现 splitString() 函数,而根本不需要 word_buffer 向量,例如:

void splitString(const string &str, char delimiter, vector<string> * out) {
    string::size_type start = 0, pos = str.find(delimiter);
    while (pos != string::npos) {
        out->push_back(str.substr(start, pos-start));
        start = pos + 1;
        pos = str.find(delimiter, start);
    }
    if (start < str.size()) {
        if (start > 0) {
            out->push_back(str.substr(start));
        } else {
            out->push_back(str);
        }
    }
}

Live Demo

void splitString(const string &str, char delimiter, vector<string> * out) {
    istringstream iss(str);
    string word;
    while (getline(iss, word, delimiter))
        out->push_back(std::move(word));
}

Live Demo

但是,即使您想手动缓冲单词,std::string 也比 std::vector&lt;char&gt; 更有意义,尤其是因为您正在输出 std::string 值:

void splitString(const string &str, char delimiter, vector<string> * out) {
    string word_buffer;
    for (string::size_type i = 0; i < str.length(); ++i) {
        if (str[i] == delimiter) {
            out->push_back(std::move(word_buffer));
            word_buffer.clear();
        } else {
            word_buffer.push_back(str[i]);
        }
    }
    if (!word_buffer.empty()) {
        out->push_back(std::move(word_buffer));
    }
}

Live Demo

【讨论】:

  • 我会更清楚地说明,主要问题是 string(const char*) 需要一个以空字符结尾的字符串,但 data() 不是以空字符结尾的。上个版本可以out-&gt;push_back(std::move(word_buffer));.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多