Побочные эффекты присвоения значения указателя переменной в C ++ - PullRequest
1 голос
/ 16 марта 2011

Я сталкиваюсь с некоторыми странными проблемами при назначении ссылки на указатель на переменную: локальный код работает правильно, но вызывает ошибки доступа к памяти в других местах:

//This works fine
Gridcell* g = model.gc;
cout << g->LC_updated << " " << g << endl;

//When I include this, the program crashes elsewhere
//But output from this line is OK
Gridcell gc = *g;
cout << "Var:" << gc.LC_updated << &gc << endl;

(Gridcell - это класс, который не имеет конструктора без аргументов) Я не эксперт по C ++, и это довольно большая внешняя библиотека, но я не могу понять, почему назначение переменной локальной области видимости может вызвать проблемы в других местах.Кто-нибудь может пролить свет на это?

Ответы [ 5 ]

3 голосов
/ 16 марта 2011

Вы не предоставили достаточно информации для решения проблемы. Нет ничего плохого в том, что вы написали код.

Я предполагаю, что Gridcell не хватает надлежащего конструктора копирования, поэтому, когда gc выходит из области видимости, он удаляет вещи, на которые *g все еще ссылается.

Эта строка кода:

Gridcell gc = *g;

- это то место, где вызывается конструктор копирования. По сути это эквивалентно тому, чтобы сказать это:

Gridcell gc(*g);

, который вызывает конструктор Gridcell::Gridcell(const Gridcell &), иначе известный как конструктор копирования. Конструктор копирования отличается тем, что если у вас его нет, компилятор автоматически сгенерирует его для вас. Автоматически сгенерированный тип обычно просто вызывает конструктор копирования каждой отдельной переменной-члена, включая указатели. Для базовых типов, таких как int или Foo *, конструктор копирования просто создает точную копию.

Например, если у вас есть такой код:

class Foo {
 public:

   Foo() : msg_(new char[30]) { strcpy(msg_, "I'm Foo!"); }
   ~Foo() { delete [] msg_; }

 private:
   char *msg_;
};

void aFunction(Foo *aFoo)
{
   Foo myfoo = *aFoo;
}

void anotherFunction()
{
   Foo localfoo;
   aFunction(&localfoo);
}

Это потерпит крах. localfoo выделит символьный массив. Строка Foo myfoo = *aFoo вызовет конструктор копирования, который сделает прямую копию указателя. Затем будет вызван деструктор для myfoo и память будет освобождена. Затем будет вызван деструктор для localfoo и память снова будет освобождена, что приведет к сбою во многих системах.

1 голос
/ 16 марта 2011

Вы должны убедиться, что Gridcell имеет правильный конструктор копирования.Это требуется только в том случае, если Gridcell вручную управляет ресурсами, что почти никогда не должно иметь место.Если это так, вам понадобится Большая тройка .

Если вам нужна более конкретная помощь, опубликуйте определение класса Gridcell вместе с конструкторами и деструктором, еслиони у вас есть.

Если вы не собираетесь копировать, вы можете использовать ссылку:

Gridcell & gc = *g;
cout << "Var:" << gc.LC_updated << &gc << endl;

Использование такое же, но оно не будет создавать копию.

0 голосов
/ 16 марта 2011

Эта строка:

GridCell gc = *g;

Делает копию экземпляра, указанную g. Если GridCell не определяет конструктор копирования (конструктор сигнатуры GridCell(const GridCell& other)), компилятор предоставляет копию по умолчанию, которая копирует каждый элемент. Для элемента, имеющего тип указателя, это просто копия указателя, и, следовательно, и gc, и экземпляр, на который указывает g, будут указывать на одну и ту же память.

Когда переменная gc выходит из области видимости, вызывается ее деструктор. Если класс GridCell управляет некоторой памятью, не предоставляет конструктор копирования (или он неверен) и освобождает память в своем деструкторе, то экземпляр GridCell, на который указывает g, будет указывать на освобожденную память после этот момент.

Как правило, когда класс управляет некоторой памятью, он должен переопределять деструктор, конструктор копирования и оператор присваивания. Это правило трех .

Обратите внимание, что назначение также выполнит нарезку, если g указывает на подкласс GridCell, что также может привести к другим проблемам. Если вы хотите использовать переменную gc в качестве псевдонима, чтобы не нужно было использовать *g везде, вам следует рассмотреть возможность использования ссылки (или const-ссылки) вместо создания копии (особенно, если объект делает менеджер большим количеством память, копия может быть дорогой).

0 голосов
/ 16 марта 2011

@ Space_C0wb0y верно.

Представьте, что в вашем классе Gridcell есть атрибуты, которые являются указателями (массивами или объектами, которые явно выделяются через new).

Когда выЕсли для объекта типа Gridcell используется другой объект, конструктор копирования по умолчанию создает копию поверхностный .Он копирует значение этих указателей, но не создает новые объекты / массивы для новых атрибутов вашего нового объекта.

Если вы разделите

cout << "Var:" << gc.LC_updated << &gc << endl;

на две строки:

cout << "&gc" <<&gc << endl;
cout << "Var:" << gc.LC_updated << endl;

Вы, вероятно, увидите, что ошибка переходит к строке, где указана ваша ссылка LC_updated (я не знаю, что это такое).

Проверьте ваши конструкторы, чтобы увидеть, как она инициализируется.

0 голосов
/ 16 марта 2011

Является ли model.gc локальной переменной?

Если это так, то когда он выходит из области видимости, он перестает существовать.И любые указатели на него больше не действительны.

...