3

嗨,我正在阅读 C++ 入门第 5 版,我想我在 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 的块结束时,q被销毁。q但是没有释放指向的内存q是内置指针而不是shared_ptr。而且除非我们显式地写delete q,否则相应的内存不会被释放。
  2. 现在,在新块中,我们使用 q 创建了一个临时 shared_ptr。但这个暂时是独立的p。所以当这个内部块结束时,临时被破坏,因此内存被释放。但请注意,它p仍然指向已释放的相同内存。所以p现在是一个悬空指针,并且p在语句中使用int foo=*p是未定义的。

我认为这是对为什么 p 是悬空指针的正确解释,也是应该存在的更正。有人可以确认这是正确的还是我做错了什么?

4

3 回答 3

1

第六次印刷时,您指出的内容略有不同,如下:

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

这里, p, q, 和local都指向同一个内存。因为plocal是彼此独立创建的,所以每个引用计数为 1。当内部块结束时,local被销毁。因为local的引用计数为 1,所以它指向的内存将被释放。这使得pq变成了一个悬空指针;当我们尝试使用pq未定义时会发生什么。此外,当p被销毁时,指向该内存的指针将被第二次删除。

我认为作者的勘误页不再管理。

于 2021-10-03T08:34:14.883 回答
1

C++ Primer 5th edition 以及您的解释在试图解释这个程序时犯了一个常见错误。注意声明:

shared_ptr<int>(q);

创建一个新的临时命名q,而不是使用q作为参数创建一个新的临时到 ashared_ptr的构造函数。下面的示例代码显示了这一点:

#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<int>{q};代替时,您的解释的第二点才是正确的shared_ptr<int>(q);

这表示:

  1. 由于作者使用shared_ptr<int>(q); 了一个名为 is created 的局部变量q,它是一个智能指针,而不是来自外部范围的内置指针。

  2. 此局部变量与外部范围q无关,也与外部范围无关,因此当此局部超出范围时,将调用 shared_ptr 的析构函数。但请注意未释放外部or指向的内存。pqqpq

  3. 所以之后当我们写int foo = *p;的时候没有未定义的行为。

下面是显示定义本地命名而不是临时使用作为参数的代码。shared_ptr<int>(q);qq

#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;
}

在上面的代码中,如果我们尝试访问 localq的值,*q那么我们将得到未定义的行为(这可能会使程序/分段错误崩溃),因为我们正在取消引用空指针。如果我们删除它*q,那么程序就没有未定义的行为。

现在,即使在本书的下一版(第 6 版)中,作者也在使用auto local = shared_ptr<int>(q);他本可以用来shared_ptr<int>{q};说明他的观点的时间。

于 2021-10-05T05:47:31.750 回答
1

正如您正确指出的那样,代码中的文本描述和注释都不适合代码。它们更符合如下代码:

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

如果我猜的话,我会说这就是示例最初的样子,然后有人决定引入原始指针,却没有意识到它也需要更改注释和文本。

于 2021-03-07T11:00:26.487 回答