Поведение деструктора по умолчанию для ссылки c ++ - PullRequest
1 голос
/ 29 апреля 2020

Я искал по inte rnet, но я не могу найти какие-либо ресурсы по поведению деструктора по умолчанию при ссылке.

Пример:

struct A{
   int &a;
   A(int&i): a(i){}
}
void f(){
    int* i = new int;
    A* a = new A(*i);
    delete a; // is i been destroyed?
}

В обоих случаях, где я могу найти ресурсы по этому поводу? Или может кто-нибудь объяснить, пожалуйста, как он себя ведет?

Ответы [ 4 ]

2 голосов
/ 29 апреля 2020

Ссылки не являются объектами. У них нет деструкторов. У них есть жизни. У объектов тоже есть время жизни, как и у любого другого типа в C ++.

Разница в том, что, когда заканчивается время жизни объекта, вызывается его деструктор. Но когда время жизни любого другого типа заканчивается, их ресурсы просто освобождаются.

В случае ссылки, ресурс, который они содержат, является адресом памяти, указывающим на другое значение. Не само значение. Поэтому, когда он освобождается, значение остается прежним. Адрес сохраненной памяти - это то, что высвобождается, а не значение.

Когда вы используете new / delete, вы вручную указываете, когда начинается время жизни (для нового) и когда оно заканчивается (при удалении).

В вашем примере ваши времена жизни X и Y будут следующими:

struct A{
   int &a;
   A(int&i): a(i){}
}
void f(){
    int* i = new int; // ----------------------------------|X
    A* a = new A(i); // -------------------|Y              |X
    delete a; // is i been destroyed? // --|Y              |X
} //                                                       |X
//                                                         |X...

Как видите, время жизни X продолжается вечно, поскольку вы не удалили его вручную но вы создали это с новым. Это означает, что значение, которое хранится в i, все равно будет действительным после строки, в которой вы удаляете a.

PD: Вместо этого вы должны были написать new A(*i), иначе вы получите компилятор ошибка.

2 голосов
/ 29 апреля 2020

Может быть, станет понятнее, если вы исправите код, чтобы он компилировался. Вы не можете передать указатель на метод, который хочет ссылку. Вы должны разыменовать указатель в первую очередь (что является довольно вводящим в заблуждение выражением в этом контексте). Таким образом, вы фактически передаете нормальную переменную.

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

Если вы понимаете, после этого становится намного яснее, что деструктору не будет иметь смысла уничтожать указанное значение. У него нет никаких гарантий, что он был создан динамически и что его нужно уничтожить. (Деструкторы даже не delete автоматически становятся членами, которые являются указателями. Вы должны сделать это явно.)

1 голос
/ 29 апреля 2020

Если коротко, он не уничтожен, и вы получаете утечку памяти.

Теперь вопрос в том, какое поведение вам действительно нужно. Могут быть разные стратегии управления владением объектами. Лучше всего использовать умные указатели вместо необработанных указателей, по крайней мере, для объектов, для которых важно владение. Например, вы можете использовать shared_ptr:

struct A{
   std::shared_ptr<int> a;
   A(std::shared_ptr<int> i): a(i){}
}
void f(){
    std::shared_ptr<int> i(new int);
    std::shared_ptr<A> a(new A(i));
}

Этот пример открывает другие вопросы: следует ли использовать значение, указанное в i, где-нибудь еще? Если он переживет объект a?

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

1 голос
/ 29 апреля 2020

Уничтожение ссылки ничего не делает - она ​​НЕ уничтожает указанный объект. Это становится ясно, когда вы помните, что мы передаем ссылки на большинство методов. Методы не уничтожают указанные объекты. Указатели одинаковы. Уничтожение указателя не уничтожает объект, на который он указывает.

В вашем случае, int, на который указывает i, пропущено.

...