В C++ Primer 5th edition обнаружена ошибка shared_ptr <int>
Привет, я читаю пятое издание праймера C++ и думаю, что заметил одну ошибку в разделе shared_ptr. Сначала я пишу код и объяснение, которое они дали. Затем я напишу, что, по моему мнению, является ошибкой, и что, по моему мнению, происходит на самом деле. Код выглядит следующим образом:
shared_ptr<int> p(new int(42));// reference count is 1
int *q = p.get();// ok: but don't use q in any way that might delete its pointer
{//new block started
shared_ptr<int>(q);
}// block ends, q is destroyed, and the memory to which q points is freed
int foo = *p;// undefined; the memory to which p points was freed
Они дали следующее объяснение:
В этом случае и p, и q указывают на одну и ту же память. Поскольку они были созданы независимо друг от друга, каждый имеет счетчик ссылок 1. Когда блок, в котором был определен q, заканчивается, q уничтожается. Уничтожение q освобождает память, на которую указывает q. Это превращает p в висящий указатель, а это означает, что то, что происходит, когда мы пытаемся использовать p, не определено. Более того, когда p уничтожается, указатель на эту память будет удален во второй раз.
Теперь я думаю, что ошибка - это утверждение: « Когда блок, в котором был определен q, заканчивается, q уничтожается. Уничтожение q освобождает память, на которую указывает q. », А также рассуждения, которые они привели, почему p является висящим указателем, ошибочны. Ниже я объясняю, почему p является висящим указателем и почему первый процитированный оператор является ошибкой.
- Когда блок, в котором был определен q, заканчивается, он уничтожается. Но память, к которой относятся точки, не освобождается, поскольку это встроенный указатель, а не shared_ptr. И если мы явно не напишем delete
q
соответствующая память не будет освобождена. - Теперь внутри нового блока мы создали временный shared_ptr с помощью q. Но это временное не зависит от. и поэтому, когда этот внутренний блок заканчивается, временное уничтожается, и, следовательно, память освобождается. Но обратите внимание, что это все еще указывает на ту же самую память, которая была освобождена. Итак, теперь висячий указатель и использование
p
в заявленииint foo=*p
не определено.
Я думаю, что это правильное объяснение того, почему p является висящим указателем, а также исправление, которое должно быть там. Может ли кто-нибудь подтвердить, правильно ли это или я что-то делаю не так?
3 ответа
C++ Primer 5th edition, а также ваше объяснение сделали одну распространенную ошибку, пытаясь объяснить эту программу. Обратите внимание, что заявление:
shared_ptr<int>(q);
создает новый временный объект с именем вместо создания нового временного, используя в качестве параметра для
#include <iostream>
using namespace std;
struct NAME
{
int p = 0;
NAME()
{
std::cout<<"default constructor"<<std::endl;
}
NAME(int d): p(d)
{
std::cout<<"d: "<<d<<" p: "<<p<<std::endl;
}
NAME(const NAME& n)
{
std::cout<<"const copy constructor"<<std::endl;
}
NAME(NAME& n)
{
std::cout<<"non const copy constructor"<<std::endl;
}
~NAME(){
std::cout<<"destructor: "<<p<<std::endl;
}
};
int main()
{
cout << "Hello World" << endl;
NAME (4);//calls converting constructor
//after the completion of the above full statement the temporary is destroyed and hence you get a destructor call in the output
NAME k;//calls default constructor
std::cout<<"k.p: "<<k.p<<std::endl;
NAME(l);//this creates a temporary named l instead of creating a temporary using l as parameter to NAME's constructor ( in particular,default constructor)
NAME{l};//this creates a temporary using l as parameter to NAME's constructor with non-const copy constructor
return 0;
}
Второй пункт вашего объяснения верен только в том случае, если мы используем вместо.
Это означает:
Поскольку автор использовал это, это означает, что создается имя локальной переменной, которое является интеллектуальным указателем, а не встроенным указателем из внешней области.
Эта локальная переменная не имеет ничего общего с обоими или с внешней областью видимости, и, следовательно, когда эта локальная переменная выходит за пределы области видимости, вызывается деструктор shared_ptr. Но обратите внимание на память, к которой внешнее
или баллы не высвобождаются и остаются действительными. Итак, потом, когда мы напишем
не существует неопределенного поведения.
Ниже приведен код, который показывает, что
#include <iostream>
#include <memory>
using namespace std;
int main()
{
cout << "Hello World" << endl;
shared_ptr<int> p(new int(42)); // reference count is 1
int *q = p.get(); // ok: but don't use q in any way that might delete its pointer
std::cout<<"q's address "<<&q<<std::endl;
std::cout<<"p's address "<<&p<<std::endl;
{ // new block
shared_ptr<int>(q);
std::cout<<"new q's address "<<&q<<std::endl;
std::cout<<"new q's value "<<(*q)<<std::endl;//this produces segmentation fault
} // block ends, local is destroyed
int foo = *p; // this is ok
return 0;
}
В приведенном выше коде, если мы попытаемся получить доступ к локальному
Теперь даже в следующем издании книги (6-е издание) автор использует
Как вы правильно заметили, текстовое описание и комментарии в коде не подходят к коду. Они больше соответствуют такому коду:
shared_ptr<int> p(new int(42));// reference count is 1
{//new block started
shared_ptr<int> q(p.get());
}// block ends, q is destroyed, and the memory to which q points is freed
int foo = *p;// undefined; the memory to which p points was freed
Если бы я догадывался, я бы сказал, что так сначала выглядел пример, а затем кто-то решил ввести необработанный указатель, не осознавая, что это также потребует изменений в комментариях и тексте.
В шестой печати содержание, которое вы указали, немного изменилось, а именно:
shared_ptr<int> p(new int(42)); // reference count is 1
int *q = p.get(); // ok: but don't use q in any way that might delete its pointer
{ // new block
// undefined: two independent shared_ptrs point to the same memory
auto local = shared_ptr<int>(q);
} // block ends, local is destroyed, and the memory to which p and q points is freed
int foo = *p; // undefined; the memory to which p points was freed
Здесь,, и все указывают на одну и ту же память. Поскольку и были созданы независимо друг от друга, каждый имеет счетчик ссылок 1. Когда внутренний блок заканчивается, он уничтожается. Потому что
local
счетчик ссылок равен 1, память, на которую он указывает, будет освобождена. Это превращает и в висячие указатели; что происходит, когда мы пытаемся использовать илиq
не определено. Более того, когдаp
уничтожается, указатель на эту память будет удален во второй раз.
Я думаю, что страница с ошибками автора больше не ведется.