struct AAA
{
AAA()
{
std::cout << __FUNCTION__ << std::endl;
}
~AAA()
{
std::cout << __FUNCTION__ << std::endl;
}
};
void deleterAAA(AAA* aaa)
{
// Сюда попадаем раньше, чем в aaa()
std::cout << __FUNCTION__ << std::endl;
delete aaa;
}
typedef boost::shared_ptr<AAA> AAAPtr;
AAAPtr getAAAPtr()
{
std::cout << __FUNCTION__ << std::endl;
return AAAPtr(new AAA, deleterAAA);
}
AAA* getAAA(AAAPtr a)
{
std::cout << __FUNCTION__ << std::endl;
return a.get();
// Здесь вызывается deleterAAA !!! :maniac:
}
void aaa(AAA* a)
{
std::cout << __FUNCTION__ << std::endl;
// Здесь объект, на который указывает a, уже разрушен :maniac:
}
int main()
{
std::cout << __FUNCTION__ << std::endl;
// По идее здесь getAAAPtr() возвращает временный объект типа AAAPtr,
// который должен дожить до конца вызова функции aaa(),
// и который должен "держать" обеъкт AAA,
// т.е. в aaa() должен попасть "живой" объект AAA
aaa(getAAA(getAAAPtr()));
}
Вывод:
main
getAAAPtr
AAA::AAA
getAAA
deleterAAA AAA::~AAA
aaa
Специально сверился со Священным Писанием:
12.2/3
Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created.
12.2/4
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression.
The first context is when a default constructor is called to initialize an element of an array...
12.2/5
The second context is when a reference is bound to a temporary...
1.9/12
A full-expression is an expression that is not a subexpression of another expression. If a language construct |
is defined to produce an implicit call of a function, a use of the language construct is considered to be an |
expression for the purposes of this definition. Conversions applied to the result of an expression in order to |
satisfy the requirements of the language construct in which the expression appears are also considered to be |
part of the full-expression. [Example:
struct S { |
S(int i): I(i) { } |
int& v() { return I; } |
private: |
int I; |
}; |
S s1(1); //full-expression is call of S::S(int) |
S s2 = 2; //full-expression is call of S::S(int) |
void f() { |
if (S(3).v()) // full-expression includes lvalue-to-rvalue and |
// int to bool conversions, performed before |
// temporary is deleted at end of full-expression |
{ } |
} |
—end example]
И на что, скажите, надеяться в этой жизни???
03.10.06 14:05: Перенесено модератором из 'C/C++. Прикладные вопросы' — Кодт
Здравствуйте, _nn_, Вы писали:
__>Здравствуйте, remark, Вы писали:
R>>Вот минимальный код, приводящий к ошибке:
__><skip>
__>Может я недопонимаю. __>Ваш код эквивалентнен:
__>
__>typedef int AAA;
__>typedef boost::shared_ptr<AAA> AAAPtr;
__>AAAPtr f()
__>{
__> return AAAPtr(new int)); // 5. создается объект
__>} // 6. Вызов конструктора копирования, объект жив. (компилятор оптимизирует его обычно)
__>AAA* g()
__>{
__> return f().get(); // 2. вызов f
__> // 7. boost::shared_ptr<AAA>::get
__> // 8. Объект умирает после вызова get, так как область видимости его ограниченна ";".
__>} // 9. Возврат мусора.
__>int main()
__>{
__> AAA* p = g(); // 1. вызов g
__>}
__>
Нет. Мне надо, что бы объект был жив только в строке 2, не далее.
Т.е. тогда, когда ещё не разрушился временный объект, возвращаемый f().
Я использую голый указатель (AAA*) в той же строчке (до , где создаётся временный объект AAAPtr. В твоём примере ты используешь голый указатель не в той строчке (после , где был создан временный объект AAAPtr.
Здравствуйте, remark, Вы писали:
R>AAA* getAAA(AAAPtr& a) R>{ R> std::cout << __FUNCTION__ << std::endl; R> return a.get(); R> // Здесь вызывается deleterAAA !!! R>}
R>Специально сверился со Священным Писанием:
Имхо, здесь происходит __cdecl, параметры разрушаются сразу после выхода из функции
R>И на что, скажите, надеяться в этой жизни???
На себя R>
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, remark, Вы писали:
R>>AAA* getAAA(AAAPtr& a) R>>{ R>> std::cout << __FUNCTION__ << std::endl; R>> return a.get(); R>> // Здесь вызывается deleterAAA !!! R>>}
R>>Специально сверился со Священным Писанием: V>Имхо, здесь происходит __cdecl, параметры разрушаются сразу после выхода из функции
Пусть разрушаются, но ведь ещё должен жить AAAPtr вне этой функции. Тот, из которого скопировали параметр этой функции.
А раз есть хоть один Ptr, он должен держать целевой объект.
Здравствуйте, remark, Вы писали:
R>>>Специально сверился со Священным Писанием: V>>Имхо, здесь происходит __cdecl, параметры разрушаются сразу после выхода из функции R>Пусть разрушаются, но ведь ещё должен жить AAAPtr вне этой функции. Тот, из которого скопировали параметр этой функции.
Ты не понял, он и живёт, это подверждает ссылка на него:
AA* getAAA(AAAPtr& a)
{
R>А раз есть хоть один Ptr, он должен держать целевой объект.
Так и происходит, но в твоём случае аргумент AAAPtr a, — это не оригинал, а копия, проинициализированная значением от getAAAPtr(). Оригинал продолжает жить, а вот копия его — умирает сразу после возврата из getAAA.
R>
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Константин Л., Вы писали:
КЛ>Здравствуйте, remark, Вы писали:
КЛ>это не баг. Объект создается внутри функции, а не снаружи, поэтому он не обязан жить до ;
А скопироваться в результат функции, по твоему, он не обязан?
Т.е. получается, что значения, возвращаемые функциями, вообще нельзя использовать, т.к. они разрушаются ещё внутри функции. Забавно.
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, remark, Вы писали:
R>>>>Специально сверился со Священным Писанием: V>>>Имхо, здесь происходит __cdecl, параметры разрушаются сразу после выхода из функции R>>Пусть разрушаются, но ведь ещё должен жить AAAPtr вне этой функции. Тот, из которого скопировали параметр этой функции. V>Ты не понял, он и живёт, это подверждает ссылка на него: V>
V>AA* getAAA(AAAPtr& a)
V>{
V>
R>>А раз есть хоть один Ptr, он должен держать целевой объект. V>Так и происходит, но в твоём случае аргумент AAAPtr a, — это не оригинал, а копия, проинициализированная значением от getAAAPtr(). Оригинал продолжает жить, а вот копия его — умирает сразу после возврата из getAAA.
Нет, это ты не понял
Копия-то пусть умирает, мне не жалко. Но при этом не должен вызываться deleterAAA().
deleterAAA() должен вызываться когда умирает последняя копия AAAPtr.
В моём примере внутри функции рушится одна копия AAAPtr внутри функции. Это всё правильно. С этим я согласен и ничего против не имею. Но при этом не должен вызываться deleterAAA(), т.к. живёт ещё первая копия AAAPtr вне функции.
Здравствуйте, Константин Л., Вы писали:
КЛ>это не баг. Объект создается внутри функции, а не снаружи, поэтому он не обязан жить до ;
что-то вы ребята не то говорите. remark использует shared_ptr, поэтому делетер вызавается когда разрушается последний экземпляр.
по стадарту (ссылки были представлены) результат getAAAPtr() должен прожить до конца выражения.
Здравствуйте, remark, Вы писали:
R>Нет, это ты не понял R>Копия-то пусть умирает, мне не жалко. Но при этом не должен вызываться deleterAAA().
Насчёт не должен, этого никто не сможет гарантировать, особенно на "необычных" компиляторах
В конечном итоге, всё зависит от компилятора и его версии R>deleterAAA() должен вызываться когда умирает последняя копия AAAPtr.
Я залез в дебри Boost и в дебуге после контруирования AAAPtr a, a->pn->pi_->use_count_/weak_count_ = 1. Я так понимаю что должно быть 2? R>В моём примере внутри функции рушится одна копия AAAPtr внутри функции. Это всё правильно. С этим я согласен и ничего против не имею. Но при этом не должен вызываться deleterAAA(), т.к. живёт ещё первая копия AAAPtr вне функции.
Я только объяснил как работает, вы лишь можете со мной не согласиться..
R>>>
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, remark, Вы писали:
R>>Нет, это ты не понял R>>Копия-то пусть умирает, мне не жалко. Но при этом не должен вызываться deleterAAA(). V>Насчёт не должен, этого никто не сможет гарантировать, особенно на "необычных" компиляторах V>В конечном итоге, всё зависит от компилятора и его версии
Я бы не стал использовать компилятор, под которым не будет работать:
f1(f2());
А тут практически такой случай... ну немного посложнее.
R>>deleterAAA() должен вызываться когда умирает последняя копия AAAPtr. V>Я залез в дебри Boost и в дебуге после контруирования AAAPtr a, a->pn->pi_->use_count_/weak_count_ = 1. Я так понимаю что должно быть 2? R>>В моём примере внутри функции рушится одна копия AAAPtr внутри функции. Это всё правильно. С этим я согласен и ничего против не имею. Но при этом не должен вызываться deleterAAA(), т.к. живёт ещё первая копия AAAPtr вне функции. V>Я только объяснил как работает, вы лишь можете со мной не согласиться..
Ну да, судя по всему компилятор чего-то наоптимайзил. Убрал кое-какие-копирования. И в итоге получилась такая лажа...
R>>>>
Здравствуйте, remark, Вы писали:
R>Вот минимальный код, приводящий к ошибке:
R>И на что, скажите, надеяться в этой жизни???
Гипотеза, откуда растут ноги у этого бага.
Видимо компилятор заточен под оптимизацию такого кода:
f1(f2());
В таком коде не обязательно, что бы значение, возвращаемое f2() жило до конца выполнения expression.
Надо что бы оно только попало в f1(). И поэтому, видимо, компилятор конструирует значение, возвращаемое f2() сразу "в параметре" f1(). Поэтому оно и разрушается внутри f1() и не живёт до ;
Странно, что это происходит и под дебагом.
Надо так понимать, что эта ошибка будет во всех аналогичных ситуациях.
Да, точняк. Здесь тоже падает:
Насколько я понимаю, твой код должен работать вот так:
R>Вот минимальный код, приводящий к ошибке:
R>
[snip]
R>AAAPtr getAAAPtr()
R>{
R> std::cout << __FUNCTION__ << std::endl;
R> return AAAPtr(new AAA, deleterAAA); //a) создается временный AAAPtr, число ссылок = 1
R>}// b) при выходе, конструируется AAAPtr из временного объекта созданного в a)
// затем временный объект созданный в a) уничтожается, число ссылок = 1.
R>AAA* getAAA(AAAPtr a) //c) конструируется AAAPtr из временного объекта созданного в b)
// затем временный объект созданный в b) уничтожается, число ссылок = 1.
R>{
R> std::cout << __FUNCTION__ << std::endl;
R> return a.get();
R> // Здесь вызывается deleterAAA !!! :maniac:
R>} // при выходе временный объект созданный в c) уничтожается, число ссылок = 0, вызывается deleterAAA
R>
[snip]
Таким образом, поведение вполне соответсвует стандарту.
The last good thing written in C was Franz Schubert's Symphony No. 9.
Здравствуйте, crable, Вы писали:
C>Насколько я понимаю, твой код должен работать вот так:
R>>Вот минимальный код, приводящий к ошибке:
R>>
C>[snip]
R>>AAAPtr getAAAPtr()
R>>{
R>> std::cout << __FUNCTION__ << std::endl;
R>> return AAAPtr(new AAA, deleterAAA); //a) создается временный AAAPtr, число ссылок = 1
R>>}// b) при выходе, конструируется AAAPtr из временного объекта созданного в a)
C>// затем временный объект созданный в a) уничтожается, число ссылок = 1.
R>>AAA* getAAA(AAAPtr a) //c) конструируется AAAPtr из временного объекта созданного в b)
C>// затем временный объект созданный в b) уничтожается, число ссылок = 1.
R>>{
R>> std::cout << __FUNCTION__ << std::endl;
R>> return a.get();
R>> // Здесь вызывается deleterAAA !!! :maniac:
R>>} // при выходе временный объект созданный в c) уничтожается, число ссылок = 0, вызывается deleterAAA
R>>
C>[snip]
C>Таким образом, поведение вполне соответсвует стандарту.
А что за лишее уничтожение в точке с ?
Как бы там ни было, каждый scope должен давать одинаковое количество созданий и разрушений объектов.
А у тебя в функции getAAA() на одно создание объекта, почему-то 2 разрушения.
C>[snip]
R>>AAA* getAAA(AAAPtr a) //c) конструируется AAAPtr из временного объекта созданного в b)
C>// затем временный объект созданный в b) уничтожается, число ссылок = 1.
на основаниии чего временный объект должен уничтожаться именно здесь?
R>>
C>[snip]
C>Таким образом, поведение вполне соответсвует стандарту.