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

Стратегия "получение ресурса есть инициализация" - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 11, средняя оценка - 4.73
ninja2
 Аватар для ninja2
230 / 186 / 7
Регистрация: 26.09.2012
Сообщений: 2,018
Завершенные тесты: 1
14.04.2013, 11:21     Стратегия "получение ресурса есть инициализация" #1
Здорова!
Тут вообщем новую концепцию ООП вычитал "получение ресурса есть инициализация"
Вообщем считается когда используешь исключения, то обязательно нужно соблюдать эту концепцию?
И еще считается, что этот способ очень хороший для работы с умными указателями, но как его можно применить к умным указателям?
Класс умный указатель этож как бы оболочка он токо ссылки содержит, какой там может быть ресурс, какое там освобождение, если он и так ресурсов не содержит????
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
14.04.2013, 11:21     Стратегия "получение ресурса есть инициализация"
Посмотрите здесь:

C++ "Повторная" инициализация объекта
Если в строке есть хоть один ноль - вывести в файл output.txt "YES", иначе вывести "NO"; C++
C++ Что делать с ошибкой: C2440: инициализация: невозможно преобразовать "int **" в "int *"
Как сделать, так чтобы i и j можно было вводить самому "i" И "j" в цикле, есть программа C++
C++ Заданный словарь слов. Найти в нем слова-палиндромы, то есть такие, которые одинаково читаются слева направо и наоборот, например, "АННА", "ШАЛАШ"
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
TheChosenOne
13 / 13 / 1
Регистрация: 13.09.2013
Сообщений: 113
28.06.2014, 02:30     Стратегия "получение ресурса есть инициализация" #21
DrOffset,
Цитата Сообщение от DrOffset Посмотреть сообщение
Вот такой же код не вызывает вопросов?
Ну,тут функция,переменные локальны по отношению к ней - тут то все ясно.
А вот во втором случае, откуда компилятору знать что С состоит из а и b ?
И второе,когда в куче создаешь объект - деструкторы вызываются тоже,хотя объект созданный при помощи new не входит в число локальных...

Добавлено через 8 минут
Т.е. объект созданный с помощью new не входит в область видимости try-блока (он где-то в куче). Однако когда генерируется исключение ,все его подобъекты уничтожаются.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
DrOffset
6416 / 3790 / 876
Регистрация: 30.01.2014
Сообщений: 6,575
28.06.2014, 02:34     Стратегия "получение ресурса есть инициализация" #22
Цитата Сообщение от TheChosenOne Посмотреть сообщение
Ну,тут функция,переменные локальны по отношению к ней - тут то все ясно.
Механизмы одни и те же.

Если брать твою фразу, то а и b по отношению к вложенной в foo class scope C, все остальное работает точно так же как в предыдущем примере.
Вот я тут уточнение написал:
Цитата Сообщение от DrOffset Посмотреть сообщение
созданные подобъекты принадлежат этой же области видимости, что и главный объект
На самом деле не совсем так. Чтобы не придирались к формулировке приведу такой псевдокод.
C++
1
2
3
4
5
6
7
8
void foo()
{
    C c { //class C scope
        A a;   
        B b;
    
    }
}
Потому что у класса на самом деле своя область видимости - class scope. Но в данной ситуации это не важно, я просто размотал иерархию подобъектов в плоское представление, чтобы было понятнее.

Цитата Сообщение от TheChosenOne Посмотреть сообщение
А вот во втором случае, откуда компилятору знать что С состоит из а и b ?
Как это откуда? Программист же написал класс и определил что там и как, компилятор все про это знает.

Цитата Сообщение от TheChosenOne Посмотреть сообщение
И второе,когда в куче создаешь объект - деструкторы вызываются тоже,хотя объект созданный при помощи new не входит в число локальных...
Ну это вообще сейчас не в тему. В стандарте четко разделены автоматические объекты и динамические. Мы говорим про автоматические.
С динамическими объектами деструктор не вызывается никогда, если не был вызван delete. А delete - это по сути освобождение памяти и прямой вызов деструктора.

Добавлено через 2 минуты
Цитата Сообщение от TheChosenOne Посмотреть сообщение
Т.е. объект созданный с помощью new не входит в область видимости try-блока (он где-то в куче). Однако когда генерируется исключение ,все его подобъекты уничтожаются.
Его уничтожает деструктор unique_ptr (который-то как раз автоматический). В этом вся суть умных указателей. Мы даем динамический объект под контроль автоматического, и используем механику автоматических объектов для управления ресурсами.
TheChosenOne
13 / 13 / 1
Регистрация: 13.09.2013
Сообщений: 113
28.06.2014, 02:38     Стратегия "получение ресурса есть инициализация" #23
DrOffset, дык я не использую unique_ptr ,а все равно для объекта созданного в куче,когда генерируется исключение в его конструкторе, вызываются деструкторы его подобъектов.

Псевдокод:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class C
 {
     A a;
     B b;
public:
    C():a(),b() {throw std::bad_alloc();}
}
 
int main()
{
try
{
 new C; //exeption
          //~b()
           //~a()
}
catch(...)
{
 }
DrOffset
6416 / 3790 / 876
Регистрация: 30.01.2014
Сообщений: 6,575
28.06.2014, 04:56     Стратегия "получение ресурса есть инициализация" #24
TheChosenOne, в этом случае деструктор ~С все равно не вызовется. А вот деструкторы A и B вызываются при выходе из class scope, в даном случае по исключению, a и b автоматические объекты по отношению к class scope. См. мое пояснение выше.

В общем, чтобы расставить все точки над i, приведу цитату:
15.2/1
As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects
constructed since the try block was entered.
Т.е. вот есть try блок, не важно где, в main, например:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void boo()
{ //scope 2
    C c; // scope 2.1
}
 
void foo()
{ //scope 1
    D d; //scope 1.1
    boo();
}
 
int main()
{
    try
    { //scope 0
        foo();
    }
    catch(...)
    {
    }
}
Далее следует череда вызовов, каждый из которых создает scope. scope 0 контролирует scope 1, scope 1 контролирует scope 2 и scope 1.1 (class scope), scope 2 контролирует scope 2.1. В штатном режиме деструкторы вызываются в конце scope. Class scope не совсем обычный scope (поэтому я его опустил в первом примере, но теперь думаю, что зря), отличается он тем, что в штатном режиме для автоматических объектов класса он завершается вместе с деструктором этого класса*. В остальном все точно так же как с обычным scope. Предположим конструктор С у нас генерирует исключение. Исключение - это выход из scope, значит деструкторы автоматических объектов принадлежащих этому scope должны быть вызваны. Что и происходит. Смотрим цитату из стандарта выше "destructors are invoked for all automatic objects constructed since the try block was entered". Это и есть раскрутка стека. Т.е. процесс прохода по всем вложенным scope начиная с самого глубокого и заканчивая try-блоком, и вызов деструкторов уже созданных в этих scope автоматических объектов. Теперь я думаю очевидно, что этому механизму вообще по барабану где находился объект (часть другого объекта или отдельно), главное то, что он а) автоматический и б) принадлежит одной из scope задействованных в раскрутке.

_________________
*На самом деле я встречал в IT такой же подход и к scope функций. У каждой функции есть как бы конструктор (pre function) и деструктор (post function). Но в С++ не так, в С++ функции - не объекты и поэтому такое поведение присуще только объектам классов (да это не последовательно и не очень логично, но помним про историю и про обратную совместимость с С).

Добавлено через 17 минут
И да, забыл добавить. Деструкторы вызываются в момент раскрутки. Будет ли этот вызов в конце каждого из участвующих scope или они пройдут разом в самом конце, при переходе к catch - зависит от реализации модели исключений в компиляторе. Поэтому говорить, что именно конструктор вызывает деструкторы уже созданных подобъектов - некорректно, на SOF ошиблись или дали неполный ответ.
TheChosenOne
13 / 13 / 1
Регистрация: 13.09.2013
Сообщений: 113
28.06.2014, 19:12     Стратегия "получение ресурса есть инициализация" #25
DrOffset, единственное что мне осталось не ясно - это сlass scope. Я понимаю это как "область видимости класса". Т.е. само пространство имен создаваемое классом и соответственно даже не представляю как его можно покинуть...
Или имеется ввиду та область видимости внутри объекта,пока существует объект ?

Добавлено через 1 час 0 минут
После генерации исключения,поток управления возвращается в конструктор класса и вот тут происходят чудеса:вызываются деструкторы. Затем поток управления переходит в вызвавшую конструктор функцию..

Добавлено через 6 минут
SOF:
The compiler generates some extra code as part of the exception handling mechanism that destroys subobjects that have already been constructed. This code gets executed somehow as part of the implementation of the exception handling mechanism whenever an exception propagates out of a constructor. – Brian 16 mins ago
The "Real" constructor of X will destroy the subobjects and base types if an exception occurs, regardless of scope. The C++ compiler adds this when you compile. – Mooing Duck 18 hours ago
Добавлено через 1 час 12 минут
Наверное просто стоит это принять как гарантию компилятора,а не пытаться вывести это из базовых правил...
DrOffset
6416 / 3790 / 876
Регистрация: 30.01.2014
Сообщений: 6,575
28.06.2014, 19:29     Стратегия "получение ресурса есть инициализация" #26
Цитата Сообщение от TheChosenOne Посмотреть сообщение
Или имеется ввиду та область видимости внутри объекта,пока существует объект ?
Да, я говорил о времени жизни объекта и о его влиянии на подобъекты. Перевод scope как "область видимости" не совсем полно отражает суть и поэтому я его оставил без перевода в предыдущем посте.
А вообще сlass scope в стандарте - более широкое понятие (см. 3.3.7).

Цитата Сообщение от TheChosenOne Посмотреть сообщение
После генерации исключения,поток управления возвращается в конструктор класса и вот тут происходят чудеса:вызываются деструкторы
Известно, что деструкторы вызываются в процессе раскрутки стека:
Вот например такой код:
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
#include <cstdio>
#include <stdexcept>
 
struct A
{
    A()  { puts(__PRETTY_FUNCTION__); }
    ~A()  { puts(__PRETTY_FUNCTION__); }
};
 
struct B
{
    B()   { puts(__PRETTY_FUNCTION__); }
    ~B()  { puts(__PRETTY_FUNCTION__); }
};
 
class C
{
public:
    C() : a(), b()
    {
        puts(__PRETTY_FUNCTION__);
        throw std::logic_error("");
    }
private:
    A a;
    B b;
};
 
void foo()
{
    C c;
}
 
int main(int argc, char *argv[])
{
    foo();
}
Т.е. исключение мы не ловим. И такой код печатает на gcc 4.7.2:
A::A()
B::B()
C::C()
terminate called after throwing an instance of 'std::logic_error'
Ни один из деструкторов не вызвался. Если добавить try - catch вокруг вызова foo() то получим вызов деструкторов. И вот у меня есть еще старый gcc 3.0.1 на RH7. Там ситуация иная, вывод этого же кода такой:
A::A()
B::B()
C::C()
B::~B()
A::~A()
Aborted
Вот тут как раз деструкторы вызвались в соответствии с этим: "This code gets executed somehow as part of the implementation of the exception handling mechanism whenever an exception propagates out of a constructor".
Однако, лично я не видел в стандарте каких-то конкретных указаний, что это должно происходить сразу же после выхода из конструктора. В стандарте написано только, что
As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects constructed since the try block was entered
.
А что такое handler в стандарте описано четко (15/1):
handler:
catch ( exception-declaration ) compound-statement
Т.е. скорее верно поведение 4.7.2 и процесс на SOF описан все-таки не совсем корректно.
TheChosenOne
13 / 13 / 1
Регистрация: 13.09.2013
Сообщений: 113
28.06.2014, 22:47     Стратегия "получение ресурса есть инициализация" #27
Цитата Сообщение от DrOffset Посмотреть сообщение
Ни один из деструкторов не вызвался.
Ну,в стандарте написано что независимо от того был ли раскручен стек будет вызвана ф-я terminate(). Страуструп пишет: будут ли вызываться деструкторы зависит от реализации С++.
Цитата Сообщение от DrOffset Посмотреть сообщение
As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects constructed since the try block was entered
Если объект создается динамически,тогда подобъекты явл. динамическими...(где-то встречал в стандарте) Так что этот пункт не совсем подходит. Для недосозданных объектов там же специальный пункт.
15.2/2 С++11
An object of any storage duration whose initialization or destruction is terminated by an exception will
have destructors executed for all of its fully constructed subobjects (excluding the variant members of a
union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution
and the destructor has not yet begun execution.
Цитата Сообщение от DrOffset Посмотреть сообщение
Да, я говорил о времени жизни объекта и о его влиянии на подобъекты.
Ну для того что бы закончилось время жизни объекта,надо что бы оно началось .

Добавлено через 29 минут
Вот тут важно:
Я отследил в отладчике весь путь потока управления. Например пусть ситуация такая: Создаем объект,в котором 2 подобъекта создаются нормально,а в конструкторе 3тьего появляется исключение.Смотрим на стек вызовов ф-й:
C++
1
2
3
4
5
~Obj2(); // Деструктор последнего созданного объекта
//External code
Obj3(); //Конструктор который кинул исключение
Constructor() //конструктор главного обЪекта
Main();
Т.е. поток управления передался сразу из конструктора объекта,который кинул исключение в деструктор уже созданного объекта. Это означает что механизм раскрутки стека еще не начался. Сначала будут удалены все созданные объекты. Хотелось бы подчеркнуть что вызов деструктора трактуется как External т.е внешне генерируемый. Я думаю это как раз означает что компилятор генирирует "внешний" код для удаления созданных объектов в случае исключения И значит он где-то хранит информацию какие подобъекты были уже созданы при вызове конструктора.

Добавлено через 24 минуты
Хах,для локальных объектов,которые полностью созданы вызов такой же. Выходит что компилятор генерирует специальный код всегда когда кидается исключение. При нормальном выходе из области видимости вызов в стеке не помечается как External. Т.е. с областью видимости это не связано,это лишь механизм исключений. Поскольку компилятор знает что нужно вызывать для всех созданных объектов в стеке деструкторы,а лишь затем делать pop() из стека что бы добраться до след. адреса возврата что бы перейти на уровень выше и так до тех пор пока не найдется handler.

Добавлено через 10 минут
External - это внешний код,который генерирует компилятор,а не метка для деструктора (извиняюсь за неточность)

Добавлено через 11 минут
Это можно все приписать к окончанию времени жизни объекта,но объект то создан не был -> его время жизни даже не начиналось. (это я про class scope leaving говорю )
Avazart
 Аватар для Avazart
6893 / 5133 / 250
Регистрация: 10.12.2010
Сообщений: 22,560
Записей в блоге: 17
28.06.2014, 23:05     Стратегия "получение ресурса есть инициализация" #28
Цитата Сообщение от ninja2 Посмотреть сообщение
А от если у меня есть такой класс который просто тупо как бы ссылки содержит, то что я должен в деструкторе вызвать?
Ничего.
У тебя другая проблема, что должен сделать деструктор объекта который действительно владеет памятью - он должен уведомить этот класс об удалении что бы тот обнулил эти указатели.
DrOffset
6416 / 3790 / 876
Регистрация: 30.01.2014
Сообщений: 6,575
29.06.2014, 04:09     Стратегия "получение ресурса есть инициализация" #29
Цитата Сообщение от TheChosenOne Посмотреть сообщение
Если объект создается динамически,тогда подобъекты явл. динамическими...(где-то встречал в стандарте) Так что этот пункт не совсем подходит. Для недосозданных объектов там же специальный пункт.
Да, соглашусь. В таком случае рассматривать нужно было две ситуации, динамический и автоматический объект. Для них работают разные пункты. Хотя на низком уровне механизм, обеспечивающий вызов деструкторов может быть един, а может и не быть - я считаю это зависит от реализации.

Цитата Сообщение от TheChosenOne Посмотреть сообщение
Ну для того что бы закончилось время жизни объекта,надо что бы оно началось
А оно началось, есть же понятие частично сконструированного объекта.

Цитата Сообщение от TheChosenOne Посмотреть сообщение
Я отследил в отладчике весь путь потока управления.
Какой компилятор?

Цитата Сообщение от TheChosenOne Посмотреть сообщение
Хах,для локальных объектов,которые полностью созданы вызов такой же. Выходит что компилятор генерирует специальный код всегда когда кидается исключение. При нормальном выходе из области видимости вызов в стеке не помечается как External. Т.е. с областью видимости это не связано,это лишь механизм исключений. Поскольку компилятор знает что нужно вызывать для всех созданных объектов в стеке деструкторы,а лишь затем делать pop() из стека что бы добраться до след. адреса возврата что бы перейти на уровень выше и так до тех пор пока не найдется handler.
Это все понятно, но реализация может отличаться, следовательно говорить, что во всех случаях именно так - неправильно, об этом я и вел речь выше про цитату с SOF.

Я все-таки считаю, что правильнее тут сказать "зависит от реализации" - что скрывается за описанием стандарта в конкретном компиляторе. Как я уже говорил, в стандарте весьма небогатые пояснения что именно должно делаться и как. Общие фразы, "раскрутка стека", "области видимости", это видно из предыдущих цитат. Но зато описано внешнее поведение. Поэтому можно сделать вывод, что разработчики вольны сами выбирать как именно должен работать механизм исключений на низком уровне. Главное чтобы он соответствовал требованиям стандарта.

Добавлено через 2 минуты
Цитата Сообщение от TheChosenOne Посмотреть сообщение
Это можно все приписать к окончанию времени жизни объекта,но объект то создан не был -> его время жизни даже не начиналось. (это я про class scope leaving говорю )
ИМХО, время жизни объекта началось. Т.к. большая часть свойств класса уже доступна в конструкторе. Объект находится в частично сконструированном состоянии, но это не значит что его нет.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
29.06.2014, 16:13     Стратегия "получение ресурса есть инициализация"
Еще ссылки по теме:

C++ Дана строка, в котором есть слово "да" или слово "нет". Если в нем есть слово "нет", то удалить его
Warning C4244: инициализация: преобразование "__int64" в "int", возможна потеря данных C++
C++ С++ ошибка 0xC0000005: Нарушение прав доступа при записи "0xcccccccc". Инициализация строк

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
TheChosenOne
13 / 13 / 1
Регистрация: 13.09.2013
Сообщений: 113
29.06.2014, 16:13     Стратегия "получение ресурса есть инициализация" #30
Цитата Сообщение от DrOffset Посмотреть сообщение
Я все-таки считаю, что правильнее тут сказать "зависит от реализации"
Да,я тоже так думаю
Цитата Сообщение от DrOffset Посмотреть сообщение
Какой компилятор?
Я работаю в среде MVS13 , там вроде стандартно майкрософтовский cl...
Yandex
Объявления
29.06.2014, 16:13     Стратегия "получение ресурса есть инициализация"
Ответ Создать тему
Опции темы

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