В 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 является висящим указателем и почему первый процитированный оператор является ошибкой.

  1. Когда блок, в котором был определен q, заканчивается, он уничтожается. Но память, к которой относятся точки, не освобождается, поскольку это встроенный указатель, а не shared_ptr. И если мы явно не напишем delete q соответствующая память не будет освобождена.
  2. Теперь внутри нового блока мы создали временный 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;
}

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

Это означает:

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

  2. Эта локальная переменная не имеет ничего общего с обоими или с внешней областью видимости, и, следовательно, когда эта локальная переменная выходит за пределы области видимости, вызывается деструктор shared_ptr. Но обратите внимание на память, к которой внешнее или баллы не высвобождаются и остаются действительными.

  3. Итак, потом, когда мы напишем не существует неопределенного поведения.

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

      #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 уничтожается, указатель на эту память будет удален во второй раз.

Я думаю, что страница с ошибками автора больше не ведется.

Другие вопросы по тегам