Sobes.tech
Middle

Kā var rasties atmiņas noplūde, izmantojot unique_ptr?

sobes.tech AI

Atbilde no AI

Tas ir sarežģīti, bet iespējams noteiktos scenārijos:

  1. Cikliski atkarības (kopīgs īpašums, apiet vienreizējo īpašuma modeli): Ja divi unique_ptr vada objektus, kas viens otru norāda, un neviens no tiem nav pirmais, kas tiek iznīcināts, atmiņa netiks atbrīvota. Tas pārkāpj unique_ptr kā vienīgā īpašnieka koncepciju. Šādos gadījumos labāk ir izmantot shared_ptr ar weak_ptr.

    #include <memory>
    
    struct A;
    struct B;
    
    struct A {
        std::unique_ptr<B> ptr_b;
        A() {}
        ~A() { /* A destruktors */ }
    };
    
    struct B {
        std::unique_ptr<A> ptr_a; // Tas ir cikliski atkarīgs no A
        B() {}
        ~B() { /* B destruktors */ }
    };
    
    int main() {
        auto a = std::make_unique<A>();
        auto b = std::make_unique<B>();
        // Iestata savstarpējas norādes
        // Tas radīs atmiņas noplūdi, ja īpašnieks a vai b pirmo reizi nesagrauj savu unique_ptr,
        // kas šajā gadījumā ir neiespējami dēļ cikliskās atkarības
        a->ptr_b = std::move(b);
        // Izejot no main, `a` tiks iznīcināts, bet tā destruktors A
        // mēģinās iznīcināt `ptr_b`. Tomēr, ja `ptr_b` vada objektu B,
        // un B satur `unique_ptr<A>`, kas vada sākotnējo A objektu,
        // veidojas cikliski atkarīgs, kas var novest pie noplūdes, ja tas netiek uzmanīgi pārvaldīts.
        // Šajā vienkāršotajā piemērā ar `make_unique`, paliks tikai `a` scope
        // un tā destruktors tiks izsaukts. Noplūde notiks, ja B vada A, un A vada B.
        // Šeit `b` ir pārnests uz `a->ptr_b`. `unique_ptr<B> b` tagad ir tukšs.
        // Izejot no main, `a` tiks iznīcināts. Tiks izsaukts destruktors A.
        // Tiks izsaukts `unique_ptr<B>` destruktors.
        // Tiks izsaukts B destruktors.
        // Tiks izsaukts `unique_ptr<A>` destruktors B ietvaros.
        // Cikla īpašuma problēma rodas, kad objekti nevar viens otru iznīcināt
        // dēļ tā, ka katrs vada otru.
        // Pareizs cikliska atkarība piemērs:
        // struct A { std::unique_ptr<B> b; };
        // struct B { A* a_raw_ptr; }; // B zina par A, bet to nevada
    
        // Ar `unique_ptr`, cikliski īpašumi ir ļoti neparasti un grūti tieši izveidot
        // bez skaidrām dizaina kļūdām. Parasta cikliska saite pati par sevi nesagādā noplūdi
        // ar `unique_ptr`, ja nav cikliskas īpašības.
    
        // **Reālistisks noplūdes scenārijs ar `unique_ptr` cikliskās atkarībās:**
        // Tas notiek nevis `unique_ptr` dēļ, bet valsts pārvaldības loģikas dēļ.
        // Piemēram, ja jums ir struktūra, kas *glabā* `unique_ptr` uz citu struktūru,
        // kura savukārt *glabā* `unique_ptr` uz pirmo struktūru, un jūs izveidojat šādus objektus
        // ar `new` un piešķirat tos `unique_ptr`:
        // auto obj1 = std::make_unique<A>();
        // auto obj2 = std::make_unique<B>();
        // obj1->ptr_b = std::move(obj2); // obj1 tagad vada obj2
        // obj1->ptr_b->ptr_a = std::move(obj1); // <- ŠE PROBLĒMA: obj2 mēģina vārīt obj1,
        // kas jau vada obj2.
        // Tas izraisīs izpildlaika kļūdu vai neiniicētu `unique_ptr`,
        // nevis ciklisku noplūdi kā ar `shared_ptr`.
        //
        // **Reālistiskākais noplūdes scenārijs ar `unique_ptr` cikliskās atkarībās:**
        // Ja izmantojat nevaldomu norādi vienā objektā, uz kuru norāda `unique_ptr`,
        // un šī nevaldomā norāde norāda uz objektu, kuru vada cits `unique_ptr`, un aizmirstat
        // iztīrīt nevaldomo norādi, kad otrais `unique_ptr` tiek iznīcināts agrāk.
        // Bet tas nav `unique_ptr` noplūde, bet nevaldomas norādes noplūde vai nepareiza atmiņas pārvaldība.
    
        // Uzmanību, kā `unique_ptr` var veicināt noplūdi, nevis par dizaina kļūdām ārpus tā.
        // Tarp tiešajiem veidiem - izņēmums.
    }