在为变量指定引用时,我遇到了一些奇怪的问题:本地代码正常工作,但它导致其他地方的内存访问错误:
//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是一个类,没有no-args构造函数) 我不是C ++专家,这是一个相当大的外部库,但我无法理解为什么对本地作用域变量的赋值会导致其他地方出现问题。任何人都可以对此有所了解吗?
答案 0 :(得分:3)
您没有提供足够的信息来解决问题。您发布的代码没有任何内在错误。
我的猜测是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 :(得分:1)
您需要确保Gridcell
具有正确的复制构造函数。仅当Gridcell
手动管理资源时才需要这样做,这几乎不应该是这种情况。如果适合您,则需要The Big Three。
如果您需要更具体的帮助,请发布Gridcell
的类定义,以及构造函数和析构函数(如果有的话)。
如果您不打算复制,可以使用参考:
Gridcell & gc = *g;
cout << "Var:" << gc.LC_updated << &gc << endl;
用法相同,但不会创建副本。
答案 2 :(得分:0)
model.gc是局部变量吗?
如果是这样,当它超出范围时就不再存在。任何指向它的指针都不再有效。
答案 3 :(得分:0)
@ Space_C0wb0y是对的。
想象一下,你的Gridcell
类中有属性是指针(通过new
显式分配的数组或对象)。
当您从另一个对象中分配Gridcell
类型的对象时,默认复制构造函数会生成浅副本。它会复制这些指针的值,但不会为新对象的新属性创建新的对象/数组。
如果你拆分
cout << "Var:" << gc.LC_updated << &gc << endl;
分两行:
cout << "&gc" <<&gc << endl;
cout << "Var:" << gc.LC_updated << endl;
您可能会看到故障进入您的参考LC_updated
的行(我不知道它是什么)。
检查构造函数以查看它是如何初始化的。
答案 4 :(得分:0)
这一行:
GridCell gc = *g;
g
指向实例的副本。如果GridCell
没有定义复制构造函数(签名GridCell(const GridCell& other)
的构造函数),那么编译器会提供一个默认的复制构造函数来执行每个成员的副本。对于具有指针类型的成员,这只是指针的副本,因此gc
和g
指向的实例都将指向相同的内存。
当gc
变量超出范围时,将调用其析构函数。如果GridCell
类确实管理了一些内存,不提供拷贝构造函数(或者它是不正确的),并在其析构函数中释放内存,那么GridCell
实例指向的g
实例在此之后将指向释放的记忆。
通常,当一个类管理一些内存时,它必须覆盖析构函数,复制构造函数和赋值运算符。这是rule of three。
请注意,如果g
指向GridCell
的子类,并且还可能带来其他问题,则分配也会执行切片。如果要将gc
变量用作别名而不必在任何地方使用*g
,则应考虑使用引用(或const-reference)而不是复制(特别是如果对象)经理大量的内存,复制可能是昂贵的。)