#c #c 11 #memory #memory-leaks #shared-ptr
#c #c 11 #память #утечки памяти #общий-ptr
Вопрос:
Мы знаем, что C 11 имеет shared_ptr, и у него будет проблема со ссылкой на цикл.
Я пытаюсь решить эту проблему, сбросив все элементы shared_ptrs в деструкторе класса.
Пример-1 share.cpp
:
#include <iostream>
#include <memory>
#include <string>
class A;
class B;
struct A {
A(const std::string amp;a_name) : name(a_name), b(nullptr) {
std::cout << name << " A::A b.get:" << b.get()
<< ", b.use_count:" << b.use_count() << std::endl;
}
~A() {
std::cout << name << " A::~A b.get:" << b.get()
<< ", b.use_count:" << b.use_count() << std::endl;
}
std::string name;
std::shared_ptr<B> b;
};
struct B {
B(const std::string amp;a_name) : name(a_name), a(nullptr) {
std::cout << name << " B::B a.get:" << a.get()
<< ", a.use_count:" << a.use_count() << std::endl;
}
~B() {
std::cout << name << " B::~B a.get:" << a.get()
<< ", a.use_count:" << a.use_count() << std::endl;
}
std::string name;
std::shared_ptr<A> a;
};
int main(void) {
std::shared_ptr<A> a1(new A("a1"));
std::shared_ptr<B> b1(new B("b1"));
a1->b = b1;
b1->a = a1;
{
std::shared_ptr<A> a2(new A("a2"));
std::shared_ptr<B> b2(new B("b2"));
a2->b = b2;
b2->a = a2;
a1->b = b2;
b1->a = a2;
a2->b = b1;
b2->a = a1;
}
{
std::shared_ptr<A> a3(new A("a3"));
std::shared_ptr<B> b3(new B("b3"));
a3->b = b1;
b3->a = a1;
a1->b = b3;
b1->a = a3;
a3->b = b3;
b3->a = a3;
}
return 0;
}
Выполнить: clang -std=c 11 share.cpp amp;amp; ./a.out
:
a1 A::A b.get:0x0, b.use_count:0
b1 B::B a.get:0x0, a.use_count:0
a2 A::A b.get:0x0, b.use_count:0
b2 B::B a.get:0x0, a.use_count:0
a3 A::A b.get:0x0, b.use_count:0
b3 B::B a.get:0x0, a.use_count:0
b2 B::~B a.get:0x7ff393405900, a.use_count:3
a2 A::~A b.get:0x7ff393405950, b.use_count:3
b1 B::~B a.get:0x7ff393405a40, a.use_count:2
a1 A::~A b.get:0x7ff393405a90, b.use_count:2
Мы видим утечку памяти из-за ссылки на цикл. Итак, моя идея такова: я сбрасываю все элементы shared_ptrs во всех деструкторах моих классов. Тогда у нас есть пример-2 share.cpp
:
#include <iostream>
#include <memory>
#include <string>
class A;
class B;
struct A {
A(const std::string amp;a_name) : name(a_name), b(nullptr) {
std::cout << name << " A::A b.get:" << b.get()
<< ", b.use_count:" << b.use_count() << std::endl;
}
~A() {
std::cout << name << " A::~A before reset b.get:" << b.get()
<< ", b.use_count:" << b.use_count() << std::endl;
b.reset();
std::cout << name << " A::~A after reset b.get:" << b.get()
<< ", b.use_count:" << b.use_count() << std::endl;
}
std::string name;
std::shared_ptr<B> b;
};
struct B {
B(const std::string amp;a_name) : name(a_name), a(nullptr) {
std::cout << name << " B::B a.get:" << a.get()
<< ", a.use_count:" << a.use_count() << std::endl;
}
~B() {
std::cout << name << " B::~B before reset a.get:" << a.get()
<< ", a.use_count:" << a.use_count() << std::endl;
a.reset();
std::cout << name << " B::~B after reset a.get:" << a.get()
<< ", a.use_count:" << a.use_count() << std::endl;
}
std::string name;
std::shared_ptr<A> a;
};
int main(void) {
std::shared_ptr<A> a1(new A("a1"));
std::shared_ptr<B> b1(new B("b1"));
a1->b = b1;
b1->a = a1;
{
std::shared_ptr<A> a2(new A("a2"));
std::shared_ptr<B> b2(new B("b2"));
a2->b = b2;
b2->a = a2;
a1->b = b2;
b1->a = a2;
a2->b = b1;
b2->a = a1;
}
{
std::shared_ptr<A> a3(new A("a3"));
std::shared_ptr<B> b3(new B("b3"));
a3->b = b1;
b3->a = a1;
a1->b = b3;
b1->a = a3;
a3->b = b3;
b3->a = a3;
}
return 0;
}
Выполнить clang -std=c 11 share.cpp amp;amp; ./a.out
:
a1 A::A b.get:0x0, b.use_count:0
b1 B::B a.get:0x0, a.use_count:0
a2 A::A b.get:0x0, b.use_count:0
b2 B::B a.get:0x0, a.use_count:0
a3 A::A b.get:0x0, b.use_count:0
b3 B::B a.get:0x0, a.use_count:0
b2 B::~B before reset a.get:0x7fdf23405900, a.use_count:3
b2 B::~B after reset a.get:0x0, a.use_count:0
a2 A::~A before reset b.get:0x7fdf23405950, b.use_count:3
a2 A::~A after reset b.get:0x0, b.use_count:0
b1 B::~B before reset a.get:0x7fdf23405a40, a.use_count:2
b1 B::~B after reset a.get:0x0, a.use_count:0
a1 A::~A before reset b.get:0x7fdf23405a90, b.use_count:2
a1 A::~A after reset b.get:0x0, b.use_count:0
Мы видим, что ссылка на цикл исправлена!
Могу ли я использовать такой шаблон проектирования для решения проблемы с ссылками на цикл shared_ptr в проекте C ? например, я просто сбрасываю все shared_ptrs в деструкторах классов в проекте C .
Комментарии:
1. При наличии ссылочного цикла ваши деструкторы никогда не будут запущены, поэтому изменение кода в деструкторах вам не поможет. Лучшим решением было бы решить, какие из ваших указателей являются указателями «обратного направления» (например, b-> a в цикле a-> b-> a) и сделать их типа
weak_ptr
или даже обычным указателем в стиле C, чтобы они не увеличивали количество ссылок.
Ответ №1:
Короткий ответ: Нет, потому shared_ptr::reset()
что уменьшает количество использования так же, как это делает деструктор общего указателя.
Длинный ответ: прочитайте свои задания.
Давайте сначала рассмотрим случай A2 / B2. После вашего назначения у вас есть 2 «цепочки» объектов. B2-> A1->B2 и A2-> B1-> A2. Общие указатели A1 / B1 имеют значение use_count равное 2 (один для цепочки, один для локальной переменной). Количество использований общих указателей A2 / B2 равно 1 (для цепочки)
Итак, затем вы запускаете 3-й блок
a1->b = b3; // After this assignment,
// you've broken the A1->B2 chain, so now B2's use
// count is zero and will be destroyed
b1->a = a3; // Same as above, for the B1->A2 chain, destroying A2
a3->b = b3; // This just assigns A3 to B3
b3->a = a3; // And back B3, forming the A3->B3->A3 chain
Теперь, когда это выходит за рамки, A3 / B3 по-прежнему указывают друг на друга и пропускаются. Единственными ссылками на объекты A1 / B1 являются локальные переменные, которые затем выходят за пределы области видимости.
Итак, вы видите, что объект B2, а затем объект A2 уничтожаются при выполнении назначений, а затем уничтожаются B1 и A1. Объекты A3 / B3 никогда не уничтожаются.
Комментарии:
1.вы правы,
a3
b3
сообщение деструктора не выводится на консоль, поэтому их деструктор никогда не вызывается при выходе из области видимости. утечка памяти.