提问人:Luchian Grigore 提问时间:8/5/2011 最后编辑:CommunityLuchian Grigore 更新时间:8/5/2011 访问量:633
C++ 常量引用成员,扩展对象的生存期
C++ const reference member extending lifetime of object
问:
这与昨天发布的一个问题有关。
class A
{
public:
mutable int x;
A()
{
static int i = 0;
x = i;
i++;
std::cout << " A()" << std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
}
void foo() const
{
x = 1;
};
};
class B
{
public:
const A & a;
B(const A & a) : a(a)
{
std::cout << " B()" << std::endl;
}
~B()
{
std::cout << "~B()" << std::endl;
}
void doSomething()
{
a.foo();
};
};
int main()
{
B b((A()));
b.doSomething();
}
现在,a 的析构函数在调用 doSomething 之前被调用。但是,尽管该函数基本上更改了 A 的成员,但调用仍然有效。不是同一个实例吗?不会创建其他 A。我使用了 A 构造函数中的静态来跟踪它。谁能解释一下?
答:
这是未定义的行为,因此没有语言标准解释。
但是,析构函数不会对存储的内存区域执行任何操作,因此,如果您稍后查看该值,该值可能仍然存在。或者,如果您尝试写入该地址,该地址仍然存在。你只是不被允许这样做。A
x
- 您的引用在 ~A() 之后无效,并且是未定义的行为
- ~A() 另外调用 A 所有成员的析构函数
例如,尝试这样做
class B
{
public:
const std::string & a;
B(const std::string & a) : a(a)
{
std::cout << " B()" << std::endl;
}
~B()
{
std::cout << "~B()" << std::endl;
}
void doSomething()
{
std::cout << "a = " << a << std::endl;
};
};
int main()
{
B b(std::string("I love C++ so much!"));
b.doSomething();
}
薄熙来是对的。
此外,您可以检查存储“A”的地址,这应该确认该地址尚未被重用(请记住,析构函数释放(“释放”)内存,但不会遍历数据结构,将所有位设置回 0;这将是低效的)。
例如,如果您发现 A 存储在堆栈的顶部,那么您很幸运,您的后续函数调用没有传入参数,因为这会覆盖 A 的内存区域。
扩展薄熙来的回答。
对于临时存在,将在堆栈上保留空间。只要语义需要临时存在,这个空间实际上是保留的,然后可以重用于其他东西。
如果你试图在内存被重用后使用它,你会观察到一种奇怪的行为(未定义行为的定义是任何事情都可能发生)。事实上,你运气好了,记忆仍然存在,处于你期望的状态。
例:
#include <iostream>
struct A {
A(): p(0) {}
~A() { if (p) { std::cout << *p << "\n"; } }
int* p;
};
int bar0();
void bar1(int i);
int main() {
A a;
{
int x = 4; a.p = &x;
}
{
int y = bar0(); bar1(y);
}
}
int bar0() { return 7; }
void bar1(int i) { std::cout << i << "\n"; }
在这里,编译器可能会选择重用 for 的空间,或者只是做任何它想做的事情,因此你实际上是在打印垃圾。x
y
以下是 gcc 4.3.4(和 4.5.1)的输出(由 ideone 提供):
7
4
这意味着该空间不会与那些...
上一个:运算符 [] 重载
下一个:在 C++ 中等待通知
评论