Co to jest "safely-derived pointer"?

0

Cześć!
Mam pytanko, czytam książkę o C++ i jest w niej zapis taki sam jak znalazłem na stronie podanej w źródle. Chodzi mi konkretnie o tą część:

Lokacyjna funkcja operator new() jest najprostszym alokatorem tego rodzaju. Jej definicja znajduje się w standardowym nagłówku :

void*operator new (size_t sz, void* p) noexcept; // lokuje obiekt o rozmiarze sz w p
void*operator new[](size_t sz, void* p) noexcept; // lokuje obiekt o rozmiarze sz w p
void operator delete (void* p, void*) noexcept; // jeśli (p), *p jest niepoprawny
void operator delete[](void* p, void*) noexcept; // jeśli (p), *p jest niepoprawny

Operatory lokacyjne delete tylko informują system usuwania nieużytków, że usunięty wskaźnik nie jest już bezpiecznie derywowany (ang. safely-derived).

źródło: https://strefainzyniera.pl/artykul/963/pamiec-wolna-i-zarzadzanie-pamiecia

I tutaj pojawia mi się pytanie, co to jest "safely-derived"? Co oznacza, że "wskaźnik nie jest już bezpiecznie derywowany"? Oraz dodatkowo, co to jest system usuwania nieużytków w C++?

Z góry dziękuję za pomoc :)

3

W praktyce nigdy nie widziałem, żeby ktoś implementował:

void operator delete (void* p, void*) noexcept;

ponieważ i tak chyba nikt tego nie woła (choć mogę się mylić).

A jest to spowodowane tym, że domyślna implementacja takiego operatora nie robi nic link

void operator delete ( void* ptr, void* place ) noexcept; (13) (since C++11)

  1. Called by the standard single-object placement new expression if the object's constructor throws an exception.
    The standard library implementation of this function does nothing.

Trudno jest mi też sobie wyobrazić żeby robiła coś. Jedyne co ten operator mógłby robić to odrejestrowanie wskaźnika z jakiegoś systemu zarządzania pamięcią, przez co, system nie będzie próbował odwołać się do tego wskaźnika.

To poniekąd odpowiada na Twoje pytanie czym jest safely-derived pointer value.

Tak jak piszą tutaj: https://stackoverflow.com/q/25216568

Weźmy taki kod:

#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
#include <cstdint>

std::vector< uintptr_t > g_ptrs;

void print_ptrs()
{
    printf("ptrs [\n");
    for( const uintptr_t p : g_ptrs )
    {
        printf( "\tptr: %zd\n", p );
    }
    printf("]\n");
}

class Foo
{
public:
    Foo()
    {
        printf("Foo constructor\n");
    }

    ~Foo()
    {
        printf("Foo destructor\n");
    }

    static void* operator new  ( std::size_t count, void* ptr )
    {
        printf("operator new\n");

        g_ptrs.push_back( reinterpret_cast< uintptr_t >( ptr ) );

        return ptr;
    }

    static void operator delete  ( void* ptr, void* place ) noexcept
    {
        printf("operator delete\n");

        g_ptrs.erase( std::remove( g_ptrs.begin(), g_ptrs.end(), reinterpret_cast< uintptr_t >( ptr ) ), g_ptrs.end() );
    }
};

int main()
{
    alignas( alignof( Foo ) ) char buffer[ sizeof( Foo ) ];
    Foo *foo = new ( &buffer ) Foo(); // operator new + Foo constructor

    print_ptrs();

    foo->~Foo(); // Foo destructor
    Foo::operator delete( foo, &buffer ); // operator delete - nigdy nie widziałem w żadnym kodzie żeby to było wołane, wołany jest tylko destruktor

    print_ptrs();
}

https://godbolt.org/z/67jPMcWe4

1

co to jest system usuwania nieużytków w C++?

Nie istnieje nic takiego w C++ jako języku. Tutaj kontekst artykułu jest istotny, gdzie autor przy okazji tłumaczenia placement new/delete próbował wyjaśnić przeznaczenie tego drugiego posługując referencją do czegoś, o czym przeciętny czytelnik tego artykułu nie będzie miał pojęcia. A po resztę cofnij się jeszcze raz do postu @mwl4.

2

Z tego co widzę, to jest jeszcze jeden use case takiego operatora.

https://stackoverflow.com/a/222615

class Pool {
public:
    Pool() { /* implementation details irrelevant */ };
    virtual ~Pool() { /* ditto */ };

    virtual void *allocate(size_t);
    virtual void deallocate(void *);

    static Pool *Pool::misc_pool() { return misc_pool_p; /* global MiscPool for general use */ }
};

class ClusterPool : public Pool { /* ... */ };
class FastPool : public Pool { /* ... */ };
class MapPool : public Pool { /* ... */ };
class MiscPool : public Pool { /* ... */ };

// elsewhere...

void *pnew_new(size_t size)
{
   return Pool::misc_pool()->allocate(size);
}

void *pnew_new(size_t size, Pool *pool_p)
{
   if (!pool_p) {
      return Pool::misc_pool()->allocate(size);
   }
   else {
      return pool_p->allocate(size);
   }
}

void pnew_delete(void *p)
{
   Pool *hp = Pool::find_pool(p);
   // note: if p == 0, then Pool::find_pool(p) will return 0.
   if (hp) {
      hp->deallocate(p);
   }
}

// elsewhere...

class Obj {
public:
   // misc ctors, dtors, etc.

   // just a sampling of new/del operators
   void *operator new(size_t s)             { return pnew_new(s); }
   void *operator new(size_t s, Pool *hp)   { return pnew_new(s, hp); }
   void operator delete(void *dp)           { pnew_delete(dp); }
   void operator delete(void *dp, Pool*)    { pnew_delete(dp); }

   void *operator new[](size_t s)           { return pnew_new(s); }
   void *operator new[](size_t s, Pool* hp) { return pnew_new(s, hp); }
   void operator delete[](void *dp)         { pnew_delete(dp); }
   void operator delete[](void *dp, Pool*)  { pnew_delete(dp); }
};

// elsewhere...

ClusterPool *cp = new ClusterPool(arg1, arg2, ...);

Obj *new_obj = new (cp) Obj(arg_a, arg_b, ...);

Ale czy ma to sens to nie wiem. Wydaje mi się, że może być trudno później ze zwalnianiem pamięci z poola (jak to opakować w jakiś smart pointer np. żeby poprawnie działało)

1 użytkowników online, w tym zalogowanych: 0, gości: 1