C++ 常量引用成员,扩展对象的生存期

C++ const reference member extending lifetime of object

提问人:Luchian Grigore 提问时间:8/5/2011 最后编辑:CommunityLuchian Grigore 更新时间:8/5/2011 访问量:633

问:

这与昨天发布的一个问题有关。

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 构造函数中的静态来跟踪它。谁能解释一下?

C++语言

评论

0赞 Nawaz 8/5/2011
Luchian:我添加了昨天问题的链接。希望你指的是我的问题。

答:

6赞 Bo Persson 8/5/2011 #1

这是未定义的行为,因此没有语言标准解释。

但是,析构函数不会对存储的内存区域执行任何操作,因此,如果您稍后查看该值,该值可能仍然存在。或者,如果您尝试写入该地址,该地址仍然存在。你只是不被允许这样做。Ax

0赞 tim 8/5/2011 #2
  • 您的引用在 ~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();
    }
0赞 GigaQuantum 8/5/2011 #3

薄熙来是对的。

此外,您可以检查存储“A”的地址,这应该确认该地址尚未被重用(请记住,析构函数释放(“释放”)内存,但不会遍历数据结构,将所有位设置回 0;这将是低效的)。

例如,如果您发现 A 存储在堆栈的顶部,那么您很幸运,您的后续函数调用没有传入参数,因为这会覆盖 A 的内存区域。

0赞 Matthieu M. 8/5/2011 #4

扩展薄熙来的回答。

对于临时存在,将在堆栈上保留空间。只要语义需要临时存在,这个空间实际上是保留的,然后可以重用于其他东西。

如果你试图在内存被重用后使用它,你会观察到一种奇怪的行为(未定义行为的定义是任何事情都可能发生)。事实上,你运气好了,记忆仍然存在,处于你期望的状态。

例:

 #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 的空间,或者只是做任何它想做的事情,因此你实际上是在打印垃圾。xy

以下是 gcc 4.3.4(和 4.5.1)的输出(由 ideone 提供):

7
4

这意味着该空间不会与那些...