Boost logo

Boost :

From: ÒÝÁØ Ñî (yyl_20050115_at_[hidden])
Date: 2020-07-31 16:51:20


Hi Andrey,

Sorry for Top-Post. But I searched and still don¡¯t know what is Top-Post, pardon me, and please explain a little.

Reply about :

After the destructor call, the object no longer exists, regardless of
whether the memory it used to be placed in is freed or not.
=====================================
This is true as a concept, but when we overload delete operator for global or for just one class,
we write ¡°free(ptr)¡± inside that overloaded function (at least MS¡¯ implementation of C++ works like this).
Which means, it¡¯s practically separated-able: destructor called and free() called. And if we separate the two stages,
we can get something that we can not achieve before, just like get a clean reference decreasing method,
and free all effected objects all together.

==================================================================
Case 1:

   struct A
   {
     unsigned int x = 0u;

     ~A() { x = 0xBAADF00D; }
   };

   rcgc_shared_ptr< A > p1;
   {
     rcgc_shared_ptr< A > p2(new A());
     p1 = p2;
     std::cout << p1->x << std::endl;
   }
   std::cout << p1->x << std::endl; // (1)

The above is a bug because (1) accesses the object A after its
destruction. If A::x was, say a unique_ptr, it would be null or pointing
to already freed memory.
======================
Why would we assign x with a useful value in destructors ?
Unlike the predicted behavior on my side,
this Case1 I run in main function, results in
0
baadf00d
(<<std::hex<< is added before to show hex value instead of dec value)



Case 2 (which follows from Case 1)

   struct B;

   struct A
   {
     rcgc_shared_ptr< B > m_b;
     std::string m_x;

     ~A() { std::cout << m_b->m_x << std::endl; }
   };

   struct B
   {
     rcgc_shared_ptr< A > m_a;
     std::string m_x;

     ~B() { std::cout << m_a->m_x << std::endl; }
   };

   rcgc_shared_ptr< A > p(new A());
   p->m_b.reset(new B());
   p->m_b->m_a = p;

When p gets destroyed, it will destroy the object A, even though it is
still referenced by B::m_a. When A::m_b is destroyed, it will call ~B,
which will access A::m_x, which is already destroyed.
======================================
I¡¯m sorry that rcgc_shared_ptr does not provide reset function.
I didn¡¯t think of this yet, but will do.

> If you have Visual Studio and run the code which detects memory leak with
> #ifdef _WIN32
> _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
> #endif
>
> You will see no memory leak at all.

This check only shows that no memory is leaked. The problem here is not
a memory leak but incorrect memory access (potential use-after-destory
and use-after-free).
==========================================
The thing is in reality, calling destructor is not same as freeing memory.
Memory is still containing valid data if no one messes with it. And if
the free(ptr) function is not called, no one can/should mess with it.
The only thing would possible is calling the destructor for multiple times
because the rcgc_shared_ptrs requests this action. But use-after-destroy
can be still avoided.

To avoid use-after-destroy, just keep the good manner like

~A(){
  If(this->p!=nullptr)
  {
delete this->p;
this->p = nullptr;
  }
}
And everything will be safe. And also, no one should do important work
inside of destructors besides freeing and avoid double freeing, don¡¯t they?

In one word, it is another way of seeing destruction of the object.

BTW, is it correct that intuitive_ptr is another kind of COM IUnknown ?
What would you do if a reference is pointed to the object itself?




Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk