Форум программистов, компьютерный форум CyberForum.ru

delete[] *pointer vs. delete pointer и утечка памяти - C++

Восстановить пароль Регистрация
 
Cynacyn
 Аватар для Cynacyn
33 / 33 / 0
Регистрация: 02.05.2013
Сообщений: 109
09.09.2013, 15:49     delete[] *pointer vs. delete pointer и утечка памяти #1
Здравствуйте!
Есть класс "умного" указателя counted_ptr, который удаляет хранящийся в нём T* owned; только если кол-во владельцев (хранящееся по адресу int* use_count), будет равно одному, иначе кол-во владельцев уменьшается на единицу.
counted_ptr владеет классом Pointer_pairs. Статический член
C++
1
 static vector<Pointer_pairs> vpp;
класса counted_ptr позволяет отслеживать состояние всех объектов counted_ptr.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Ch 19. Ex 11. Realize counted_ptr 
//------------------------------------------------------------------------------
#include <vector>
#include <algorithm>
 
using std::vector;
//------------------------------------------------------------------------------
 
//------------------------------------------------------------------------------
template<class T>
class counted_ptr {
    T* owned;
    int* use_case;
 
    struct Pointer_pairs {
        void* arg_ptr;
        void* owned_ptr;
        int* number_of_owners;
    
        Pointer_pairs() 
            : arg_ptr(0),
              owned_ptr(0),
              number_of_owners(0)
        {}
        Pointer_pairs(void* arg, void* own, int* num) 
            : arg_ptr(arg),
              owned_ptr(own),
              number_of_owners(num)
        {}
        ~Pointer_pairs() {}
        // for sorting in reverse order. 
        // Pointer_pairs::number_of_owners==0 goes to the end
        // so we can pop_back() them from vpp
        bool operator<(const Pointer_pairs& p) {
            return (*number_of_owners)>*(p.number_of_owners);
        }
    };
 
    static vector<Pointer_pairs> vpp;
 
public:
 
    counted_ptr() : owned(0), use_case(0) { }
    counted_ptr(T* pt) : owned(0), use_case(0)
    {
        for(int i=0; i<vpp.size(); i++)
            if(pt==vpp[i].arg_ptr) {
                owned = static_cast<T*>(vpp[i].owned_ptr);
                ++(*vpp[i].number_of_owners);
                use_case=vpp[i].number_of_owners;
 
                cout << "couted_ptr(T* pt) " << this << "->" << " for " << owned << "->" << *owned << "[" << *use_case << "]" << endl;
 
                return;
            }
 
        if (pt) owned = new T(*pt);
        else owned = pt;
 
        vpp.push_back(Pointer_pairs(pt,owned,new int(1)));
        use_case = vpp[vpp.size()-1].number_of_owners;
        
        cout << "couted_ptr(T* pt) " << this << "->" << " for " << owned << "->" << *owned << "[" << *use_case << "]" << endl;
 
    }
    counted_ptr(const counted_ptr& cp) : owned(cp.owned), use_case(cp.use_case) 
    { 
        for(int i=0; i<vpp.size(); i++)
            if(cp.owned==vpp[i].owned_ptr) {
                ++(*vpp[i].number_of_owners);
                i = vpp.size();
            }
 
        cout << "couted_ptr(const counted_ptr& cp) " << this << "->" << " for " << owned << "->" << *owned << "[" << *use_case << "]" << endl;
    }
    counted_ptr& operator=(const counted_ptr& cp) 
    {
        if(this==&cp) return *this;
 
        // line 72: if this->(*use_case) == 1, it will delete owned from free store, preventing mem. leak
        counted_ptr temp(*this); 
 
        for(int i=0; i<vpp.size(); i++)
            if(owned==vpp[i].owned_ptr) {
                --(*vpp[i].number_of_owners);
                i = vpp.size();
            }
        
        for(int i=0; i<vpp.size(); i++)
            if(cp.owned==vpp[i].owned_ptr) {
                owned = cp.owned;
                ++(*vpp[i].number_of_owners);
                use_case = vpp[i].number_of_owners;
            }
 
        cout << "counted_ptr& operator=(const counted_ptr& cp) " << this << "->" << " for " << owned << "->" << *owned << "[" << *use_case << "]" << endl;
        return *this;
    }
    ~counted_ptr() {
        if(*use_case<2) {
            cout << "~couted_ptr() " << this << "->" << " for " << owned << "->" << *owned << "[" << *use_case << "]" << endl;
            for(int i=0; i<vpp.size(); i++) 
                if(owned==vpp[i].owned_ptr) {
                    *(vpp[i].number_of_owners)=-1;
                    i = vpp.size();
                }
            std::sort(vpp.begin(),vpp.end());
            vpp.pop_back();
            cout << "~couted_ptr() " << this << "->" << " for " << owned << "->" << *owned << "[" << *use_case << "]" << endl;
        //  delete [] owned;
            delete owned;
            delete use_case;
        }
        else {
            cout << "~couted_ptr() " << this << "->" << " for " << owned << "->" << *owned << "[" << *use_case << "]" << endl;
            for(int i=0; i<vpp.size(); i++) 
                if(owned==static_cast<T*>(vpp[i].owned_ptr)) {
                    --(*vpp[i].number_of_owners);
                    i = vpp.size();
                }
            }
    }
 
 
    
    int& owner_num() const {return *use_case; }
    
    T* release() {
        for(int i=0; i<vpp.size(); i++)
            if(owned==static_cast<T*>(vpp[i].owned_ptr)) {
                --vpp[i].number_of_owners;
                i = vpp.size();
                --(*use_case);
            }
    
        T* temp = owned;
        owned = 0;
        return temp;
    }
 
    T& operator*() {return *owned;}
    const T& operator*() const {return *owned;}
    T* operator->() { return owned; }
    const T* operator->() const {return owned; }
 
 
};
//------------------------------------------------------------------------------
template<class T>
vector<class counted_ptr<T>::Pointer_pairs> counted_ptr<T>::vpp;
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Вот содержание main():
C++
1
2
3
4
5
6
7
8
9
    counted_ptr<string> cp(new string("FIRST"));
    //vector<counted_ptr<string>> vcps;
    //vcps.push_back(cp);
 
    counted_ptr<string> cp2(new string("SECOND"));
    cp = cp2;
 
    cout << cp.owner_num() << endl;
    cout << cp2.owner_num() << endl;
Если в counted_ptr.h поменять строки 110 на 111, т.е. вместо delete использовать delete[], то программа будет вылетать с debug assertion failed block type is valid phead nblockuse, но если в main() раскоментировать строки 2 и 3, то программа будет выдавать ожидаемый результат как с delete так и с delete[]. По условию задачи необходимо использовать опереатор new. Можно ли очистить память выделенную при помощи new не используя delete / delete[]?

Если пользователь инициализирует объект counted_ptr при помощи указателя на массив элементов, то delete удалит память занятую только первым элементом, при это произойдет утечка памяти, правильно?
Подскажите пожалуйста как избежать утечки памяти в данной ситуации?

Заранее, спасибо!
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
09.09.2013, 15:49     delete[] *pointer vs. delete pointer и утечка памяти
Посмотрите здесь:

почему не delete (выделение памяти) C++
C++ утечка памяти и delete
C++ освобождение памяти delete
C++ delete[] статической памяти
Удаление памяти посредством delete[] C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
CheshireCat
Эксперт С++
2907 / 1235 / 78
Регистрация: 27.05.2008
Сообщений: 3,307
09.09.2013, 15:54     delete[] *pointer vs. delete pointer и утечка памяти #2
Строго соблюдай соответствие операций для единичного объекта и для массива объектов:
C++
1
2
new -> delete    // для одного объекта
new[] -> delete[]    // для массива объектов
Нарушение этого правила ведет к неопределенному поведению программы.
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
09.09.2013, 17:08     delete[] *pointer vs. delete pointer и утечка памяти #3
Cynacyn, Стоит делать как в бусте/STL.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename T>
class counted_ptr
{
  /// implementation
public:
    ~counted_ptr() { delete owned; }
};
 
template<typename T>
class counted_ptr<T[]>
{
    // implementation
public:
   ~counter_ptr() { delete[] owned; }
};
Можно конечно вместо специализации мутить всякие флажки и т.д., но это имхо несколько грустнее. Но этот вариант подойдет очевидно только для варианта, если будет вызвано нечто вроде

C++
1
counted_ptr<int[10]> p(array);
Если инициализировать именно указателем на массив элементов - определить что передали именно указатель на первый элемент массива невозможно. И тут спасет только то, что пользователь должен будет либо сам подавать флажок, либо передавать указатель на функцию (ну или объект), который нужно будет вызвать при удалении (как сделано в boost::shared_ptr к примеру).
Cynacyn
 Аватар для Cynacyn
33 / 33 / 0
Регистрация: 02.05.2013
Сообщений: 109
09.09.2013, 17:59  [ТС]     delete[] *pointer vs. delete pointer и утечка памяти #4
Спасибо!
Уже отправив сообщение, я осознал что мой класс в любом случае сохранит только первый элемент из массива, поэтому использование delete [] неуместно.
Yandex
Объявления
09.09.2013, 17:59     delete[] *pointer vs. delete pointer и утечка памяти
Ответ Создать тему
Опции темы

Текущее время: 03:37. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru