Я пытаюсь написать свой двусвязный список. Но Вальгринд говорит, что у меня здесь утечки памяти. Я понятия не имею, что может произойти в тех строках, которые мне показывает Вальгринд. Не могли бы вы помочь мне?
Я пытался изменить весь мой указатель на shared_ptr, но это не помогло.
Весь код с int main ():
#include <cstddef>
#include <iostream>
#include <vector>
#include <random>
#include <memory>
template <typename T>
class List {
private:
class ListNode {
public:
T value_;
std::shared_ptr<ListNode> prev_;
std::shared_ptr<ListNode> next_;
ListNode(T &value, std::shared_ptr<ListNode> prev, std::shared_ptr<ListNode> next)
: value_(std::move(value)) {
std::swap(next_, next);
std::swap(prev_, prev);
}
~ListNode() {
prev_ = nullptr;
next_ = nullptr;
}
};
class Iterator {
public:
Iterator(const std::shared_ptr<ListNode> ¤t) : current_(current) {
}
Iterator &operator++() {
if (current_->next_ != nullptr) {
current_ = current_->next_;
}
return *this;
}
Iterator operator++(int) {
Iterator cpy(*this);
if (current_->next_ != nullptr) {
current_ = current_->next_;
}
return cpy;
}
Iterator &operator--() {
if (current_->prev_ != nullptr) {
current_ = current_->prev_;
}
return *this;
}
Iterator operator--(int) {
Iterator cpy(*this);
if (current_->prev_ != nullptr) {
current_ = current_->prev_;
}
return cpy;
}
T &operator*() const {
return current_->value_;
}
T &operator->() const {
return current_->value_;
}
bool operator==(const Iterator &rhs) const {
return current_ == rhs.current_;
}
bool operator!=(const Iterator &rhs) const {
return current_ != rhs.current_;
}
std::shared_ptr<ListNode> current_;
};
std::shared_ptr<ListNode> begin_;
std::shared_ptr<ListNode> end_;
size_t size_ = 0;
public:
List() : begin_(nullptr), end_(nullptr), size_(0) {
}
List(List &lst) {
begin_ = nullptr;
end_ = nullptr;
size_ = 0;
for (auto x : lst) {
PushBack(x);
}
}
List(List &&lst) {
begin_ = nullptr;
end_ = nullptr;
size_ = 0;
for (auto &x : lst) {
PushBack(std::move(x));
}
lst.begin_ = nullptr;
lst.end_ = nullptr;
lst.size_ = 0;
}
~List() {
for (auto &x : (*this)) {
x.~T();
}
begin_ = nullptr;
end_ = nullptr;
size_ = 0;
}
List &operator=(List &lst) {
this->~List();
for (auto x : lst) {
PushBack(x);
}
return *this;
}
List &operator=(List &&lst) {
for (auto &x : lst) {
PushBack(std::move(x));
}
lst.begin_ = nullptr;
lst.end_ = nullptr;
lst.size_ = 0;
return *this;
}
bool IsEmpty() const {
return size_ == 0;
}
size_t Size() const {
return size_;
}
void PushBack(T &elem) {
if (end_ == nullptr) {
begin_ = std::make_shared<ListNode>(elem, nullptr, nullptr);
end_ = std::make_shared<ListNode>(elem, begin_, nullptr);
begin_->next_ = end_;
} else {
end_->value_ = elem;
end_->next_ = std::make_shared<ListNode>(elem, end_, nullptr);
end_ = end_->next_;
}
size_++;
}
void PushBack(T &&elem) {
if (end_ == nullptr) {
begin_ = std::make_shared<ListNode>(elem, nullptr, nullptr);
end_ = std::make_shared<ListNode>(elem, begin_, nullptr);
begin_->next_ = end_;
} else {
end_->value_ = std::move(elem);
end_->next_ = std::make_shared<ListNode>(elem, end_, nullptr);
auto q = end_->next_;
end_ = q;
q.reset();
}
size_++;
}
void PushFront(const T &elem) {
if (end_ == nullptr) {
begin_ = std::make_shared<ListNode>(elem, nullptr, nullptr);
end_ = std::make_shared<ListNode>(elem, begin_, nullptr);
begin_->next_ = end_;
} else {
begin_->prev_ = std::make_shared<ListNode>(elem, nullptr, begin_);
begin_ = begin_->prev_;
}
size_++;
}
void PushFront(T &&elem) {
if (end_ == nullptr) {
begin_ = std::make_shared<ListNode>(elem, nullptr, nullptr);
end_ = std::make_shared<ListNode>(elem, begin_, nullptr);
begin_->next_ = end_;
} else {
begin_->prev_ = std::make_shared<ListNode>(elem, nullptr, begin_);
begin_ = begin_->prev_;
}
size_++;
}
T &Front() {
return begin_->value_;
}
const T &Front() const {
return begin_->value_;
}
T &Back() {
return end_->prev_->value_;
}
const T &Back() const {
return end_->prev_->value_;
}
void PopBack() {
if (end_ != begin_) {
std::shared_ptr<ListNode> cur_node = end_;
if (end_->prev_) {
end_->prev_->next_ = nullptr;
end_ = end_->prev_;
size_--;
} else {
begin_ = nullptr;
end_ = nullptr;
size_--;
}
} else {
begin_.reset();
end_.reset();
begin_ = nullptr;
end_ = nullptr;
size_--;
}
}
void PopFront() {
if (begin_) {
std::shared_ptr<ListNode> cur_node = begin_;
if (begin_->next_) {
begin_->next_->prev_ = nullptr;
begin_ = begin_->next_;
size_--;
} else {
begin_ = nullptr;
end_ = nullptr;
size_--;
}
} else {
begin_ = nullptr;
end_ = nullptr;
size_--;
}
}
Iterator Begin() {
return Iterator(begin_);
}
Iterator End() {
return Iterator(end_);
}
Iterator begin() { // NOLINT
return List<T>::Iterator(begin_);
}
Iterator end() { // NOLINT
return List<T>::Iterator(end_);
}
};
int main() {
////////
List<std::shared_ptr<int>> l10;
l10.PushBack(std::make_shared<int>(0));
l10.PushBack(std::make_shared<int>(1));
l10.PushBack(std::make_shared<int>(2));
List<std::shared_ptr<int>> l20{l10};
List<std::shared_ptr<int>> l30;
l30 = l10;
std::cout << (l30.Size() == 3);
std::cout << (l20.Size() == 3);
std::cout << (l10.Front().use_count() == 3);
////////
List<std::unique_ptr<int>> l1;
l1.PushBack(std::make_unique<int>(0));
l1.PushBack(std::make_unique<int>(1));
l1.PushBack(std::make_unique<int>(2));
List<std::unique_ptr<int>> l2{std::move(l1)};
std::cout << (*l2.Front() == 0);
std::cout << (l1.Size() == 0);
std::cout << (l2.Size() == 3);
List<std::unique_ptr<int>> l3;
l3 = std::move(l2);
std::cout << (l2.Size() == 0);
std::cout << (l3.Size() == 3);
std::cout << (*l3.Front() == 0);
/////////
List<int> l;
l.PushBack(0);
l.PushBack(1);
l.PushBack(2);
auto i = l.Begin();
std::cout << (*i == 0) << std::endl;
std::cout << (*(++i) == 1) << std::endl;
std::cout << (*(++i) == 2) << std::endl;
std::cout << (*(i++) == 2) << std::endl;
std::cout << (i == l.End()) << std::endl;
std::cout << (*(--i) == 2) << std::endl;
std::cout << (*(--i) == 1) << std::endl;
std::cout << (*(i--) == 1) << std::endl;
std::cout << (i == l.Begin()) << std::endl;
i++;
std::cout << (i == ++l.Begin()) << std::endl;
}
Valgrind говорит, что я ошибаюсь в этой строке в обеих функциях push_front и push_back:
begin_ = std::make_shared<ListNode>(elem, nullptr, nullptr);
Снимок экрана с предупреждениями Valgrinid: https://yadi.sk/i/IcHkUUjEeq-YiQ У меня недостаточно репутации, чтобы опубликовать его как изображение. = (
Версия с необработанными указателями:
#pragma once
#include <cstddef>
#include <iostream>
#include <vector>
#include <random>
#include <memory>
template <typename T>
class List {
private:
class ListNode {
public:
T value_;
ListNode *prev_;
ListNode *next_;
ListNode(T &value, ListNode *prev, ListNode *next)
: value_(std::move(value)) {
std::swap(next_, next);
std::swap(prev_, prev);
}
~ListNode() {
prev_ = nullptr;
next_ = nullptr;
}
};
class Iterator {
public:
Iterator(ListNode *current) : current_(current) {
}
Iterator &operator++() {
if (current_->next_ != nullptr) {
current_ = current_->next_;
}
return *this;
}
Iterator operator++(int) {
Iterator cpy(*this);
if (current_->next_ != nullptr) {
current_ = current_->next_;
}
return cpy;
}
Iterator &operator--() {
if (current_->prev_ != nullptr) {
current_ = current_->prev_;
}
return *this;
}
Iterator operator--(int) {
Iterator cpy(*this);
if (current_->prev_ != nullptr) {
current_ = current_->prev_;
}
return cpy;
}
T &operator*() const {
return current_->value_;
}
T &operator->() const {
return current_->value_;
}
bool operator==(const Iterator &rhs) const {
return current_ == rhs.current_;
}
bool operator!=(const Iterator &rhs) const {
return current_ != rhs.current_;
}
ListNode *current_;
};
ListNode *begin_;
ListNode *end_;
size_t size_ = 0;
public:
List() : begin_(nullptr), end_(nullptr), size_(0) {
}
List(List &lst) {
begin_ = nullptr;
end_ = nullptr;
size_ = 0;
for (auto x : lst) {
PushBack(x);
}
}
List(List &&lst) {
begin_ = nullptr;
end_ = nullptr;
size_ = 0;
for (auto &x : lst) {
PushBack(std::move(x));
}
lst.begin_ = nullptr;
lst.end_ = nullptr;
lst.size_ = 0;
}
~List() {
for (auto &x : (*this)) {
x.~T();
}
begin_ = nullptr;
end_ = nullptr;
size_ = 0;
}
List &operator=(List &lst) {
this->~List();
for (auto x : lst) {
PushBack(x);
}
return *this;
}
List &operator=(List &&lst) {
for (auto &x : lst) {
PushBack(std::move(x));
}
lst.begin_ = nullptr;
lst.end_ = nullptr;
lst.size_ = 0;
return *this;
}
bool IsEmpty() const {
return size_ == 0;
}
size_t Size() const {
return size_;
}
void PushBack(T &elem) {
if (end_ == nullptr) {
begin_ = new ListNode(elem, nullptr, nullptr);
end_ = new ListNode(elem, begin_, nullptr);
begin_->next_ = end_;
} else {
end_->value_ = elem;
end_->next_ = new ListNode(elem, end_, nullptr);
end_ = end_->next_;
}
size_++;
}
void PushBack(T &&elem) {
if (end_ == nullptr) {
begin_ = new ListNode(elem, nullptr, nullptr);
end_ = new ListNode(elem, begin_, nullptr);
begin_->next_ = end_;
} else {
end_->value_ = std::move(elem);
end_->next_ = new ListNode(elem, end_, nullptr);
auto q = end_->next_;
end_ = q;
}
size_++;
}
void PushFront(const T &elem) {
if (end_ == nullptr) {
begin_ = new ListNode(elem, nullptr, nullptr);
end_ = new ListNode(elem, begin_, nullptr);
begin_->next_ = end_;
} else {
begin_->prev_ = new ListNode(elem, nullptr, begin_);
begin_ = begin_->prev_;
}
size_++;
}
void PushFront(T &&elem) {
if (end_ == nullptr) {
begin_ = new ListNode(elem, nullptr, nullptr);
end_ = new ListNode(elem, begin_, nullptr);
begin_->next_ = end_;
} else {
begin_->prev_ = new ListNode(elem, nullptr, begin_);
begin_ = begin_->prev_;
}
size_++;
}
T &Front() {
return begin_->value_;
}
const T &Front() const {
return begin_->value_;
}
T &Back() {
return end_->prev_->value_;
}
const T &Back() const {
return end_->prev_->value_;
}
void PopBack() {
if (end_ != begin_) {
ListNode *cur_node = end_;
if (end_->prev_) {
end_->prev_->next_ = nullptr;
end_ = end_->prev_;
size_--;
} else {
begin_ = nullptr;
end_ = nullptr;
size_--;
}
} else {
begin_ = nullptr;
end_ = nullptr;
size_--;
}
}
void PopFront() {
if (begin_) {
ListNode *cur_node = begin_;
if (begin_->next_) {
begin_->next_->prev_ = nullptr;
begin_ = begin_->next_;
size_--;
} else {
begin_ = nullptr;
end_ = nullptr;
size_--;
}
} else {
begin_ = nullptr;
end_ = nullptr;
size_--;
}
}
Iterator Begin() {
return Iterator(begin_);
}
Iterator End() {
return Iterator(end_);
}
Iterator begin() { // NOLINT
return Iterator(begin_);
}
Iterator end() { // NOLINT
return Iterator(end_);
}
};
int main() {
////////
List<std::shared_ptr<int>> l10;
l10.PushBack(std::make_shared<int>(0));
l10.PushBack(std::make_shared<int>(1));
l10.PushBack(std::make_shared<int>(2));
List<std::shared_ptr<int>> l20{l10};
List<std::shared_ptr<int>> l30;
l30 = l10;
std::cout << (l30.Size() == 3);
std::cout << (l20.Size() == 3);
std::cout << (l10.Front().use_count() == 3);
////////
List<std::unique_ptr<int>> l1;
l1.PushBack(std::make_unique<int>(0));
l1.PushBack(std::make_unique<int>(1));
l1.PushBack(std::make_unique<int>(2));
List<std::unique_ptr<int>> l2{std::move(l1)};
std::cout << (*l2.Front() == 0);
std::cout << (l1.Size() == 0);
std::cout << (l2.Size() == 3);
List<std::unique_ptr<int>> l3;
l3 = std::move(l2);
std::cout << (l2.Size() == 0);
std::cout << (l3.Size() == 3);
std::cout << (*l3.Front() == 0);
/////////
List<int> l;
l.PushBack(0);
l.PushBack(1);
l.PushBack(2);
auto i = l.Begin();
std::cout << (*i == 0) << std::endl;
std::cout << (*(++i) == 1) << std::endl;
std::cout << (*(++i) == 2) << std::endl;
std::cout << (*(i++) == 2) << std::endl;
std::cout << (i == l.End()) << std::endl;
std::cout << (*(--i) == 2) << std::endl;
std::cout << (*(--i) == 1) << std::endl;
std::cout << (*(i--) == 1) << std::endl;
std::cout << (i == l.Begin()) << std::endl;
i++;
std::cout << (i == ++l.Begin()) << std::endl;
}
Эта версия также прошла все тесты.
Теперь Valgrind говорит, что я ошибаюсь в строке:
begin_ = new ListNode(elem, nullptr, nullptr);
Выход Valgrind (одна из ошибок):
<error>
<unique>0x16</unique>
<tid>1</tid>
<kind>Leak_DefinitelyLost</kind>
<xwhat>
<text>128 (32 direct, 96 indirect) bytes in 1 blocks are definitely lost in loss record 23 of 25</text>
<leakedbytes>128</leakedbytes>
<leakedblocks>1</leakedblocks>
</xwhat>
<stack>
<frame>
<ip>0x4C3017F</ip>
<obj>/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so</obj>
<fn>operator new(unsigned long)</fn>
</frame>
<frame>
<ip>0x10AB0B</ip>
<obj>/home/meetch/CLionProjects/LinkedList/cmake-build-debug/LinkedList</obj>
<fn>List<std::shared_ptr<int> >::PushBack(std::shared_ptr<int>&)</fn>
<dir>/home/meetch/CLionProjects/LinkedList</dir>
<file>main.cpp</file>
<line>154</line>
</frame>
<frame>
<ip>0x109D5A</ip>
<obj>/home/meetch/CLionProjects/LinkedList/cmake-build-debug/LinkedList</obj>
<fn>List<std::shared_ptr<int> >::List(List<std::shared_ptr<int> >&)</fn>
<dir>/home/meetch/CLionProjects/LinkedList</dir>
<file>main.cpp</file>
<line>100</line>
</frame>
<frame>
<ip>0x109086</ip>
<obj>/home/meetch/CLionProjects/LinkedList/cmake-build-debug/LinkedList</obj>
<fn>test_copy_constr()</fn>
<dir>/home/meetch/CLionProjects/LinkedList</dir>
<file>main.cpp</file>
<line>484</line>
</frame>
<frame>
<ip>0x108F93</ip>
<obj>/home/meetch/CLionProjects/LinkedList/cmake-build-debug/LinkedList</obj>
<fn>main</fn>
<dir>/home/meetch/CLionProjects/LinkedList</dir>
<file>main.cpp</file>
<line>473</line>
</frame>
</stack>
<suppression>
<sname>insert_a_suppression_name_here</sname>
<skind>Memcheck:Leak</skind>
<skaux>match-leak-kinds: definite</skaux>
<sframe> <fun>_Znwm</fun> </sframe>
<sframe> <fun>_ZN4ListISt10shared_ptrIiEE8PushBackERS1_</fun> </sframe>
<sframe> <fun>_ZN4ListISt10shared_ptrIiEEC1ERS2_</fun> </sframe>
<sframe> <fun>_Z16test_copy_constrv</fun> </sframe>
<sframe> <fun>main</fun> </sframe>
<rawtext>
<![CDATA[
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: definite
fun:_Znwm
fun:_ZN4ListISt10shared_ptrIiEE8PushBackERS1_
fun:_ZN4ListISt10shared_ptrIiEEC1ERS2_
fun:_Z16test_copy_constrv
fun:main
}
]]>
</rawtext>
</suppression>
</error>