Boost logo

Boost :

From: ÒÝÁØ Ñî (yyl_20050115_at_[hidden])
Date: 2020-07-31 18:05:24


Hi Andrey,
I know wiki¡­ considering I¡¯m in China, I get timeout for wiki as well... I¡¯ll try other ways to find out the Top-Post meaning.



> 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.

The page I linked before contained a link to Wikipedia that explains it.
Here is that link:

https://en.wikipedia.org/wiki/Posting_style#Top-posting

> 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).

Right, that's how storage allocation functions work. By the time they
are invoked, the object is either not yet created or is already destroyed
===
This reply confirmed my understanding of your understanding of C++ object destruction.


> ==================================================================
> 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 ?

Does it matter? The example shows that you're accessing a destroyed
object, which you confirmed in the output below.
-----

How do you define a destroyed object? If its content is not touched
and there is still at least one pointer (in cache or map) to that allocated memory?
Just because the destructor is called?
And if you go deep into machine code, the destructor is only a caller of the
contents¡¯ destructors in cascade, plus freeing memory with free() function.

As I remember once the std::vector<T> is implemented the same way,
~PTR() and free() are for different stages. I can not find the source code now.




> 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.

The reset function has the same semantics as for shared_ptr. It doesn't
change my point though.
--
Then shall I just put it aside while conversating with you.

>> 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.

True.

> Memory is still containing valid data if no one messes with it.

Not true.

In general, accessing memory after object destruction is undefined
behavior. Whether that memory contents still contain the data that was
last present in the destroyed object is not guaranteed.
--
What if, make this a defined behavior?
Leaving undefined for any reason?


> 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.

No, it won't be safe. As an example of a real practical problem with
this code is that compilers are allowed to remove the assignment of
nullptr to the class member because the object is destroyed and no valid
code will read that member after destruction. Basically, that assignment
is dead code.

https://gcc.godbolt.org/z/rnncjn
--------------------------------------

If compiler is allowed to remove double freeing protection code or treat it as dead code,
How can other trivial or non-trivial code work safely?


> And also, no one should do important work
> inside of destructors besides freeing and avoid double freeing, don¡¯t they?

Does it matter what people do in destructors? Destructors are not
supposed to be called on a non-existing object. (BTW yes, people do
non-trivial things in destructors besides releasing resources.)

You seem to be missing the fact that C++ doesn't work with just bytes in
memory and has an actual object model.
-------------
The object model, mostly we talk about virtual function table pointer for the first
4 or 8 bytes of an object. And this is not touch as well, if you don¡¯t free that memory.
Can we agree on this ?


----
> BTW, is it correct that intuitive_ptr is another kind of COM IUnknown ?

I'm not sure I understand this question. Yes, you can use intrusive_ptr
to reference IUnknown and derived objects.

> What would you do if a reference is pointed to the object itself?

I'm not sure what use case you mean. In general, I don't have to do
anything special about cycle resolution with intrusive_ptrs because
either cycles are not possible in that code or they resolve
automatically (e.g. there is a destroy() method that explicitly resets
all pointers stored in the object before returning).
---
Well, shell we just skip this, it¡¯s not quite relevant.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost


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