Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

С++ удаление ответственность

1.9K
02 марта 2009 года
GreenRiver
451 / / 20.07.2008
Доброго дня!

Возникла проблемка: существует некий класс, назовем его TInfo, у него есть метод, который возвращает список объектов GetList() - его задача вернуть список объектов (в любом контейнере).
Вопрос заключается в том, кто будет ответственнен за удаление этого контейнера?
Если создавать контейнер в функции, то передав его наружу не получится контролировать его. Делать умный указатель внутри TInfo не очень хороший вариант - вдруг будет два запроса GetList подряд (не делать же массив умных указателей).

У меня есть такой вариант: GetList принимает по ссылки контейнер и заполняет его, таким образом видно, что ответственность лежит не на классе, а на внешней системе.

Хочется знать как вообще в С++ решается проблема ответственности при возвращении из функции сложных типов? И насколько плох/хорош мой вариант с передачей контейнера по ссылки для последующего заполнения?
3
02 марта 2009 года
Green
4.8K / / 20.01.2000
Не понятно, тебя волнует, кто будет удалять контейнер или кто будет удалять содержимое контейнера?
Не понятно, что возвращает твоя ф-ция, чем именно заполнен контейнер?

Если при каждом вызове ф-ции контейнер заполняется вновь получаемыми значениями (данные каждый раз перерасчитываются), то логичнее, чтоб их удалял тот, кто их получает.А ещё правильнее, возложить эту ответственность на сам контейнер, например, использовать обычный контейнер, содержащий данные по значению, а не по указателю.

Если же ф-ция возвращает набор референсов на некоторые хранимые данные, например, есть общий набор данных, а твоя ф-ция возвращает некоторую выборку из этого набора, то распоряжаться данными должен тот, кто их хранит.
842
02 марта 2009 года
sigmov
301 / / 16.09.2008
Цитата: GreenRiver
Хочется знать как вообще в С++ решается проблема ответственности при возвращении из функции сложных типов? И насколько плох/хорош мой вариант с передачей контейнера по ссылки для последующего заполнения?

По моему С++ на то и С++, что контроль памяти под динамические объекты целиком лежит на программисте.

Цитата:
У меня есть такой вариант: GetList принимает по ссылки контейнер и заполняет его, таким образом видно, что ответственность лежит не на классе, а на внешней системе.

Хороший вариант, но тогда внутри функции GetList() этот контейнер нужно предварительно очищать - во избежании потери памяти.

Но чтобы вообще не парится - есть варинат:
- объявить в классе TInfo статический vector<map<void*,int>> - и каждый раз, когда ваша функция "выпускает наружу" динамический объект, сохранять в вектор(Мап(его адрес, его размер)).
- в статическом деструкторе прогнать весь вектор и освободить память по указателям в заданных размерах

Таким образом ваша программа всегда будет корректно завершена.

3
02 марта 2009 года
Green
4.8K / / 20.01.2000
Цитата: sigmov

По моему С++ на то и С++, что контроль памяти под динамические объекты целиком лежит на программисте.


Ну а как же smart pointers ?

Цитата: sigmov

Но чтобы вообще не парится - есть варинат:
- объявить в классе TInfo статический vector<map<void*,int>> - и каждый раз, когда ваша функция "выпускает наружу" динамический объект, сохранять в вектор(Мап(его адрес, его размер)).
- в статическом деструкторе прогнать весь вектор и освободить память по указателям в заданных размерах

Таким образом ваша программа всегда будет корректно завершена.


1. Автор топика говорит про контейнер. Каким боком тогда здесь void* ?
2. Старайтесь (категорически) не использовать void*. Без него можно всегда (99.9%) обойтись.
3. В C++ нет статических деструкторов.
4. А если вызовов этой функции миллион? Будем добавлять память в слоты материнской платы?

11
02 марта 2009 года
oxotnik333
2.9K / / 03.08.2007
Цитата: Green

Если при каждом вызове ф-ции контейнер заполняется вновь получаемыми значениями (данные каждый раз перерасчитываются), то логичнее, чтоб их удалял тот, кто их получает.


Параллельный вопрос (про COM)
есть метод СОМ объекта, который возвращает VARIANT*
т.е.:

 
Код:
STDMETHODIMP CCOMObj::GetValue(VARIANT* Value)
{
    VARIANT val;
    val.vt = VT_BSTR;
    val.bstrVal = ::SysAllocString(_T("Come string"));
    *Value = &val;
    return S_OK;
}
кто и где должен вызвать ::SysFreeString(...) ?
87
02 марта 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: GreenRiver
Вопрос заключается в том, кто будет ответственнен за удаление этого контейнера?


Вообще, тут нужен какой-то код, так как не ясен контекст. А так можно сказать, что контейнер должен удалиться сам, как выйдет из области видимости.

Цитата: GreenRiver
У меня есть такой вариант: GetList принимает по ссылке контейнер и заполняет его, таким образом видно, что ответственность лежит не на классе, а на внешней системе.


Лучше по указателю. Так будет понятнее, что контейнер поменяется. Поэтому обычно, используются константные ссылки, чтобы не запутаться.

Но тут опять ничего не понятно без кода. Если TInfo что-то насоздавал динамически, то как об этом узнает внешняя система? Опять же лучше, чтобы контейнер сам себе выделял память, а потом сам и удалил.

842
02 марта 2009 года
sigmov
301 / / 16.09.2008
Могу предложить свой метод:
Код:
class _GC
{
    stack<void*> Pointer;
public:
    Control(void* P){this->Pointer.push(P);}
    ~_GC()
    {  
        while(!Pointer.empty()){    //-572662307 - число свободы
           
            if(*(int*)Pointer.top() != -572662307){
                cout<<Pointer.top()<<" -> free"<<endl;
                free(Pointer.top());}
            Pointer.pop();}
    }
}mGC;


int main()
{
    int *F = new int();
    mGC.Control(F);
    double *D = new double();
    mGC.Control(D);
    mGC.Control(D);
}


Есть смысл все указатели, которые потенциально могут повиснуть загружать в mGC - в конце работы программы он освободит все ссылки из стека, если они не были free ранее.
87
02 марта 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: sigmov
Могу предложить свой метод


Какой-то неудачный пример...

1. Зачем нужен контейнер, содержащий указатели на данные неизвестного типа? Я бы понял, если бы они были наследниками одного общего базового класса, в котором мы будем использовать одинаковые функции.

2. Зачем нужны указатели на такие простые типы? Что мы экономим тут?

3. Зачем нужно "число свободы"? Типа, данные, равные этому числу не будут удаляться? А смысл?

4. Почему используется free, а не delete? Не симметрично как-то.

5. Переменная, на которую указывает D, удалится 2 раза?

842
02 марта 2009 года
sigmov
301 / / 16.09.2008
Цитата: Kogrom
1. Зачем нужен контейнер, содержащий указатели на данные неизвестного типа? Я бы понял, если бы они были наследниками одного общего базового класса, в котором мы будем использовать одинаковые функции.


Запихнуть любой тип.

Цитата:
2. Зачем нужны указатели на такие простые типы? Что мы экономим тут?


Это пример. Можно более сложные указатели передавать.

Цитата:
3. Зачем нужно "число свободы"? Типа, данные, равные этому числу не будут удаляться? А смысл?


чтоб на ваш пункт 5 ответ был "нет".

Цитата:
4. Почему используется free, а не delete? Не симметрично как-то.

delet вызывает деструктор типа. Но если деструктор перекрыт private - облом и ошибка.

Цитата:
5. Переменная, на которую указывает D, удалится 2 раза?

Нет.

87
02 марта 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: sigmov
чтоб на ваш пункт 5 ответ был "нет".


В чем фокус? То есть, если я удалил переменную, то указатель на нее вернет такое число? Не знал. Можно ссылки на такую информацию?

А главное, что и практика показывает, что нет такого числа. В общем, непонятный "сборщик мусора"...

Цитата: sigmov
delet вызывает деструктор типа. Но если деструктор перекрыт private - облом и ошибка.


Ну, а зачем лезть туда, где запрещено удалять таким образом? Деструктор защитили из каких-то соображений, а не просто так. Может тут лучше исключением воспользоваться?

842
02 марта 2009 года
sigmov
301 / / 16.09.2008
Цитата: Kogrom
А главное, что и практика показывает, что нет такого числа. В общем, непонятный "сборщик мусора"...


Вы правы - я ошибался.

87
02 марта 2009 года
Kogrom
2.7K / / 02.02.2008
Эх, решил тоже создать какой-нибудь бесполезный "сборщик мусора" (точнее, создатель мусора) на основе кода от sigmov. Этот рабочий, но имеет пару недостатков:
1. Заточен под конкретный тип (семейство классов в лучшем случае).
2. Должен объявляться первым в своей области видимости.

Код:
template<class T>
class GC
{
    list<T*> pointers;
public:
    T* GetPointer()
    {
        pointers.push_back(new T());
        return pointers.back();
    }
    ~GC()
    {
        while (!pointers.empty())
        {
            delete pointers.front();
            pointers.pop_front();
        }
    }
};

struct MyClass
{
    MyClass(){cout << "MyClass()" << endl;}
    ~MyClass(){cout << "~MyClass()" << endl;}
};

GC<MyClass> mGC;
GC<double> dGC;
GC<int> iGC;

int main()
{
    double *dcp = dGC.GetPointer();
    int *icp = iGC.GetPointer();

    { // локальный контейнер с указателями
        GC<MyClass> local_mGC;
        MyClass *mcp[3];
        for(int i = 0; i < 3; ++i)
            mcp = local_mGC.GetPointer();
    }
    cout << "in main" << endl;

    MyClass *mcp[3];
    for(int i = 0; i < 3; ++i)
        mcp = mGC.GetPointer();

    return 0;
}
3
02 марта 2009 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom

Лучше по указателю. Так будет понятнее, что контейнер поменяется. Поэтому обычно, используются константные ссылки, чтобы не запутаться.


А я бы рекомендовал передавать по ссылке. Меняется или нет контейнер, должно следовать из названия ф-ции и упоминанием слова const.
А вот указатель по-хорошему придется ещё и на 0 проверять.

IMHO указатели стоит применять только там, где без них не обойтись. В остальных случаях - ссылки.

3
02 марта 2009 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom
Эх, решил тоже создать какой-нибудь бесполезный "сборщик мусора" (точнее, создатель мусора) на основе кода от sigmov.


Взгляните на уже изобретенные велосипеды:
http://www.boost.org/doc/libs/1_36_0/libs/ptr_container/doc/ptr_container.html

87
02 марта 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Green
А я бы рекомендовал передавать по ссылке. Меняется или нет контейнер, должно следовать из названия ф-ции и упоминанием слова const.
А вот указатель по-хорошему придется ещё и на 0 проверять.


Тут рассматриваются тонкости, которые можно понять только когда много раз наступишь на те или другие грабли. Может у меня будет больше случаев, когда я вроде передавал переменную в функцию по значению, а оказалось, что передалось по ссылке. Может больше будет случаев, когда я передам вместо "правильного" указателя ноль или "удаленный" указатель (извините за термины).

В обоих случаях может быть заложена мина замедленного действия.

Цитата: Green
Взгляните на уже изобретенные велосипеды:
http://www.boost.org/doc/libs/1_36_0/libs/ptr_container/doc/ptr_container.html


Ну, я свой велосипед изобрел ради шутки. Не думал, что такое может пригодиться. А вообще, за boost надо будет как-нибудь серьезно взяться. Осталось немного "подрасти" и придумать себе хороший стимул.

То, что увидел мельком (клоны и прочее) почему-то напомнило Java.

1.9K
03 марта 2009 года
GreenRiver
451 / / 20.07.2008
Всем большое спасибо за ответы!

Вообще дело вот как обстоит: функция ищет в системе определенные классы и возвращает ссылки на них, т.е. std::list<TSomeClass*>.
Получается надо удалить только сам контейнер.

to Kogrom: а как сделать, чтобы удалялось при выходе из области видимости? Поломал голову, но так и не придумал. Если только из функции возвращать auto_ptr<list<TSomeClass*>>, страшновато выглядит :)

P.S. спасибо всем за ценные замечания по поводу передачи контейнера в функцию :) Кое-чего я не учел :)
3
03 марта 2009 года
Green
4.8K / / 20.01.2000
Цитата: GreenRiver

to Kogrom: а как сделать, чтобы удалялось при выходе из области видимости? Поломал голову, но так и не придумал. Если только из функции возвращать auto_ptr<list<TSomeClass*>>, страшновато выглядит :)


Я за него :)

Если борешься за производительность, то контейнер для заполнения лучше передавать снаружи в качестве аргумента ф-ции. Так у тебя, кстати, появляется возможность ещё и возвращать результат выполнения операции, например true/false.

Если уж так нужен указатель, то сделай typedef:
typedef auto_ptr<list<TSomeClass*>> MyListPtr;

1.9K
03 марта 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: Green

Если уж так нужен указатель, то сделай typedef:
typedef auto_ptr<list<TSomeClass*>> MyListPtr;


Вот так мне очень нравится :) И это выглядит более правильно с точки зрения привычного положения вещей - функция просто возвращает список, а не просит список для заполнения.

Кстати не думаю, что вариант с умным указателем будет "медленнее", особенно с учетом того, что при передаче контейнера в функцию надо делать ряд проверок. Производительность у меня на первом плане (система моделирования) так что надо будет подумать, что лучше :)

87
03 марта 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: GreenRiver
Вообще дело вот как обстоит: функция ищет в системе определенные классы и возвращает ссылки на них, т.е. std::list<TSomeClass*>.
Получается надо удалить только сам контейнер.

to Kogrom: а как сделать, чтобы удалялось при выходе из области видимости? Поломал голову, но так и не придумал. Если только из функции возвращать auto_ptr<list<TSomeClass*>>, страшновато выглядит :)


Пять минут думал над вопросом, но так и не понял, что спрашивалось. Если переменная локальная (автоматическая), то она сама удалится при выходе из области видимости. Ничего не надо делать, если не использовать функции типа new.

1.9K
03 марта 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: Kogrom
Пять минут думал над вопросом, но так и не понял, что спрашивалось. Если переменная локальная (автоматическая), то она сама удалится при выходе из области видимости. Ничего не надо делать, если не использовать функции типа new.


Я спросил и сам же предложил варинт... просто думал может как-то по-другому можно :)

87
03 марта 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: GreenRiver
Я спросил и сам же предложил варинт... просто думал может как-то по-другому можно :)


Конечно, можно. Не пойму, зачем тут нужен auto_ptr.

 
Код:
// входим в блок
{
    list<TSomeClass*> myList;
    // используем myList;
}
// Область видимости закончилась - myList разрушился сам

Ну, можно извратиться и написать что-то типа:
 
Код:
// входим в блок
{
    list<TSomeClass*> *myListPtr = new list<TSomeClass*>(0);
    // используем myListPtr;
}
// Область видимости закончилась - *myListPtr не разрушился

Но зачем так делать? Не вижу логики.
3
03 марта 2009 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom
Конечно, можно. Не пойму, зачем тут нужен auto_ptr.


Видимо, GreenRiver, имел в виду иное:

 
Код:
auto_ptr< list<TSomeClass*> > func()
{
    auto_ptr< list<TSomeClass*> > myListPtr( new list<TSomeClass*>(0) );
    return myListPtr;
}


только это не совсем красиво и удобно
я бы все же сделал так:
 
Код:
void func( list<TSomeClass*>& );
1.9K
03 марта 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: Green
Видимо, GreenRiver, имел в виду иное:


Да, именно так! Неудобство и правда в таком варианте есть: возвращается не простой указатель - и если это будет например vector, то не получится воспользоваться оператором [] (только если в явном виде: operator[]() ).

 
Код:
MyListPtr Vector = Info.GetList();
    Vector[0]; // ошибка, что и понятно в общем-то
    Vector->operator [](0); // только так

Так что остановлюсь на передаче контейнера внутрь функции для заполнения.
87
03 марта 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: GreenRiver
Да, именно так! Неудобство и правда в таком варианте есть: возвращается не простой указатель - и если это будет например vector, то не получится воспользоваться оператором [] (только если в явном виде: operator[]() ).
 
Код:
MyListPtr Vector = Info.GetList();
    Vector[0]; // ошибка, что и понятно в общем-то
    Vector->operator [](0); // только так

Так что остановлюсь на передаче контейнера внутрь функции для заполнения.


можно и по другому (с обычными указателями - точно)

 
Код:
MyListPtr Vector = Info.GetList();
    (*Vector)[0]; //

Но тоже мало красивого.
1.9K
03 марта 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: Kogrom
можно и по другому (с обычными указателями - точно)


Проверил - можно и с "умными". Все же покрасивее, чем operator[](0) :)

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог