Вызов clear () для вектора фактически не удаляет данные в data ()? - PullRequest
1 голос
/ 08 июня 2019

Резюме

Кажется, что просто вызвать 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
"2" из "252" переносится в "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.

1 Ответ

3 голосов
/ 08 июня 2019

Метод вектора data() возвращает необработанный указатель на выделенный массив вектора в памяти.clear() уничтожает содержимое этого массива, если необходимо, и устанавливает size() вектора в 0, но не перераспределяет сам массив и, следовательно, не изменяет capacity() вектора.Вызов метода shrink_to_fit() вектора перераспределяет массив так, что его capacity() совпадает с size(), если это возможно (shrink_to_fit() носит рекомендательный характер и не гарантирует , чтобы действительно что-либо делать).

Кроме того, при построении std::string из указателя char* данные 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<char>тем более что вы выводите 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

...