Создание указателей внутри для l oop приводит к указанию на тот же адрес памяти - PullRequest
0 голосов
/ 17 февраля 2020

У меня есть объект, который я пытаюсь продублировать любое количество раз с небольшими изменениями в этом объекте. Я хочу хранить указатели на эти дублированные объекты в std::vector. Я использую for l oop, чтобы попытаться достичь результата. Однако я заметил, что std::vector<T *> указывает на тот же адрес после выхода из l oop. Я попробовал эту попытку дублировать объекты с std::string, и я вижу те же эффекты. Вот мой фрагмент кода.

int main() {
  auto name = new std::string("fido");

  const int size = 3;
  std::vector<std::string *> names;
  names.reserve(size);

  for (int i = 0; i < size; i++) {
    std::string n = *name + " " + std::to_string(i);
    names.push_back(&n);
  }

  // nothing from this loop prints out
  for (auto n : names) {
    std::cout << *n << std::endl;
  }

  return 0;
}

Когда я помещаю код в отладчик, я вижу, что names хранит все указатели на один и тот же адрес памяти: 0x7ffffffeead0. Любые идеи о том, что я делаю здесь неправильно?

Я изменил код для хранения вектора строк, а затем для l oop через каждую строку, создавая указатель на адрес каждой строки следующим образом. Этот подход также не работает. Вектор указателей все еще указывает на один и тот же адрес; хотя в этот раз все они указывают на адрес последней строки.

int main() {
  auto name = new std::string("fido");

  const int size = 3;
  std::vector<std::string *> pointers;
  std::vector<std::string> names;

  names.reserve(size);
  pointers.reserve(size);

  for (int i = 0; i < size; i++) {
    std::string n = *name + " " + std::to_string(i);
    names.push_back(n);
  }

  for (int i = 0; i < size; i++) {
    auto n = names.at(i);
    auto p = &n;
    pointers.push_back(p);
  }

  for (auto n : names) {
    std::cout << n << std::endl;
  }

  for (auto n : pointers) {
    std::cout << *n << std::endl;
  }

  return 0;
}

Тем не менее, в третьей попытке я модифицирую код следующим образом. Обратите внимание, что я использую оператор & для доступа к элементу по индексу. Здесь я получаю std::vector<std::string *>, который указывает на разные адреса (правильно соответствует вектору строк).

int main() {
  auto name = new std::string("fido");

  const int size = 3;
  std::vector<std::string *> pointers;
  std::vector<std::string> names;

  names.reserve(size);
  pointers.reserve(size);

  for (int i = 0; i < size; i++) {
    std::string n = *name + " " + std::to_string(i);
    names.push_back(n);
  }

  for (int i = 0; i < size; i++) {
    auto p = &names.at(i); // using address operator like this "works"
    pointers.push_back(p);
  }

  for (auto n : names) {
    std::cout << n << std::endl;
  }

  for (auto n : pointers) {
    std::cout << *n << std::endl;
  }

  return 0;
}

Наконец, я наконец-то получил этот более лаконичный пример для работы. Здесь я создаю новый указатель на каждый l oop.

int main() {
  auto name = new std::string("fido");

  const int size = 3;
  std::vector<std::string *> names;
  names.reserve(size);

  for (int i = 0; i < size; i++) {
    auto x = new std::string(*name + " " + std::to_string(i));
    names.push_back(x);
  }

  for (auto n : names) {
    std::cout << *n << std::endl;
  }

  return 0;
}

Есть ли что-то на for-loops и указатели, которые мне здесь не хватает? Буду признателен за любые указатели (без каламбура) на определение правил для C ++.

1 Ответ

6 голосов
/ 17 февраля 2020

Однако, что я заметил, так это то, что std :: vector указывает на тот же адрес после выхода из l oop.

Есть какие-либо идеи о том, что я делаю здесь неправильно?

for (int i = 0; i < size; i++) {
    std::string n = *name + " " + std::to_string(i);

Здесь n - автоматическая c переменная. n - как и все объекты с автоматическим c хранилищем, будут уничтожены в конце области, то есть в конце итерации.

    names.push_back(&n);

Здесь вы возьмите адрес автоматического объекта c и сохраните его внутри вектора.

}

Здесь итерация заканчивается и начинается следующая. n уничтожено, а указатель в векторе недействителен. На следующей итерации создается новый объект n. Поскольку предыдущий n был уничтожен, эту же память можно использовать для следующего объекта.

for (auto n : pointers) {
    std::cout << *n << std::endl;

В этой строке вы пытаетесь получить доступ к несуществующей строке через недопустимый указатель и поведение программы не определены.

Один и тот же шаблон сохраняется во второй и третьей попытках.

Во всех ваших примерах, включая четвертую, вы теряете память.


У меня есть объект, который я пытаюсь продублировать сколько угодно раз

Вы можете хранить строки в std::vector<std::string>. Вы можете продублировать такой вектор как этот:

std::vector<std::string> names;
// insert names here

std::vector<std::string> duplicates = names;
...