C++ Primer 第 5 版中发现的错误 shared_ptr<int>

Error spotted in C++ Primer 5th edition shared_ptr<int>

您好,我正在阅读 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

他们给出的解释如下:

In this case, both p and q point to the same memory. Because they were created independently from each other, each has a reference count of 1. When the block in which q was defined ends, q is destroyed. Destroying q frees the memory to which q points. That makes p into a dangling pointer, meaning that what happens when we attempt to use p is undefined. Moreover, when p is destroyed, the pointer to that memory will be deleted a second time.

现在我认为错误是语句“当定义 q 的块结束时,q 是 destroyed.Destroying q 释放 q 指向的内存。 " 以及他们给出的为什么 p 是悬空指针的推理是错误的。以下是我为什么 p 是悬空指针以及为什么第一个引用的语句是错误的原因。

  1. 当定义 q 的块结束时,q 被销毁。但是 q 指向的内存没有被释放,因为 q 是一个内置指针而不是 shared_ptr。并且除非我们明确地写 delete q 相应的内存将不会被释放。
  2. 现在,在新块中,我们使用 q 创建了一个临时 shared_ptr。但是这个临时独立于p。因此,当这个内部块结束时,临时文件被销毁,因此内存被释放。但请注意 p 仍然指向已释放的相同内存。所以 p 现在是一个悬挂指针,在语句 int foo=*p 中使用 p 是未定义的。

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

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

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

Here, p, q, and local all point to the same memory. Because p and local were created independently from one another, each has a reference count of 1. When the inner block ends, local is destroyed. Because local’s reference count is 1, the memory to which it points will be freed. That makes p and q into a dangling pointers; what happens when we attempt to use p or q is undefined. Moreover, when p is destroyed, the pointer to that memory will be deleted a second time.

我认为 author's errata page 不再受管理。

C++ Primer 第 5 版以及您的解释在尝试解释该程序时犯了一个常见错误。注意语句:

shared_ptr<int>(q);

创建一个名为 q 的新临时文件,而不是使用 q 作为 shared_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);,因此创建了一个名为 q 的局部变量,它是一个智能指针,而不是来自外部作用域的内置指针。

  2. 这个局部变量 q 与外部范围的 pq 无关 并且因此当这个本地 q 超出范围时 shared_ptr 的析构函数被调用。但请注意外部 pq 指向的内存 未释放 .

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

下面的 code 显示 shared_ptr<int>(q); 定义了一个名为 q 的局部变量,而不是使用 q 作为参数的临时变量。

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

在上面的代码中,如果我们尝试使用 *q 访问本地 q 的值,那么我们将得到 未定义的行为(这可能会使program/segmentation 错误),因为我们取消引用了一个空指针。如果我们删除这个 *q 那么程序就没有未定义的行为。

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