C++ Primer 第五版智能指针的这两段描写是不是有误?

2021-12-23 20:02:17 +08:00
 tracker647
p404:

到目前为止,我们使用过的类中,分配的资源都与对应对象生存期一致。例如,每个
vector“拥有”其自己的元素。当我们拷贝一个 vector 时,原 vector 和副本 vector
中的元素是相互分离的:
vector<string> v1;
{
vector<string> v2 = {"a","an","the"};
v1 = v2;
}
由一个 vector 分配的元素只有当这个 vector 存在时才存在。当一个 vector 被销毁
时,这个 vector 中的元素也都被销毁。

上段字面理解了下以为是说 v1 是引用 v2 的元素,若 v2 内的元素过了块被销毁,v1 的元素也就没了,但是自己代码敲了一遍发现能正常使用:

vector<string> v1;
{
vector<string> v2= {"a","an","the"};
v1 =v2;
}

for(string s: v1){
cout <s << " ";
}

结果:an an the

p414
...也不要用 get 初始化一个智能指针或者为智能指针赋值

智能指针类型定义了一个名为 get 的函数(参见表 12.1),它返回一个内置指针,
指向智能指针管理的对象。此函数是为了这样一种情况而设计的:我们需要向不能使用智
能指针的代码传递一个内置指针。使用 get 返回的指针的代码不能 delete 此指针。
虽然编译器不会给出错误信息,但将另一个智能指针也绑定到 get 返回的指针上是
错误的:
shared ptr<int> p(new int (42) ); //引用计数为 1
int *q = p.get(); 1/正确:但使用 q 时要注意,不要让它管理的指针被释放
( //新程序块
//未定义:两个独立的 shared-ptr 指向相同的内存
shared ptr<int>(q);
//程序块结束, q 被销毁,它指向的内存被释放
int foo = *p; //未定义: p 指向的内存已经被释放了
}
在本例中, p 和 q 指向相同的内存,由于它们是相互独立创建的,因此各自的引用计数都
是 1,当 q 所在的程序块结束时, q 被销毁,这会导致 q 指向的内存被释放。从而 p 变成
一个空悬指针,意味着当我们试图使用 p 时,将发生未定义的行为。而且,当 p 被销毁时,
这块内存会被第二次 delete.

这段自己也敲了代码,也是能正常引用 p:

shared ptrcint> p3(new int(42));
cout <<*p3 < endl;
int *q1 =p3.get();
{
shared ptr<int>(q1);
}
int foo=p33,
cout <<foo < endl;

结果:
42
42

是 C++更新导致书的描述过时了还是我看错了?
1595 次点击
所在节点    问与答
9 条回复
secondwtq
2021-12-23 20:33:04 +08:00
第一个,v1 = v2 时会把 v2 里的元素拷贝一份给 v1 ,所以你 v1 是可以用的
第二个,有两种可能,一是虽然 UB 了,但是并没出明显的错误。二是你遇到了 C 坑爹语法里的一个坑,你定义了一个名为 q1 ,类型为 shared_ptr<int>的变量,而非用 q1 初始化了一个匿名的 shared_ptr<int>值。由于你这个奇葩排版实在是难以判断,我怀疑你要是把整段程序都写出来没准还能看到什么 studio.h ,void 面函数之类的
secondwtq
2021-12-23 20:34:30 +08:00
另外生命周期的问题最好是搞一个有各种 ctor 和 dtor 的类,这样看着清楚一点。
wevsty
2021-12-23 20:34:32 +08:00
1 、我认为书中的表达是没有说错的,是你理解出现了偏差。
原书中的意思是只有 vector 还在生存周期的时候这些元素才能正常访问,如果 vector 销毁,则其中的元素应该被认为已经被销毁了。

2 、书中的描述很明确。如果你使用 get()拿到了原始指针,并且拿原始指针初始化了另一个智能指针,在第二个智能指针退出生命周期的时候内存已经被 delete 过一次了。使用已经被 delete 过的内存是一种未定义的行为,也许编译器在处理上并没有重新使用这段内存空间,所以你仍然能访问得到原始的结果,但是这只是一种巧合(偶然),并非一定会发生的事件。

最后友情提示:发帖请注意排版
mmm159357456
2021-12-23 21:02:33 +08:00
第一个用等号是拷贝赋值,用括号同理
tracker647
2021-12-23 21:12:43 +08:00
@secondwtq 图省事直接用 OCR 扫了书上代码处理了下。。。。想想应该直接 md 弄好代码的
halfdb
2021-12-23 22:05:30 +08:00
第二个:
从 C++标准的角度说,解引用已经释放的指针是未定义行为,也就是上面说的 UB 。这意味着 C++标准不规定这个行为会引发什么。所以你这一次虽然访问到了原来的数据,但是 C++不保证你每次都会得到这样的结果。

从实现的角度来说,一个指针指向的空间被释放,并不代表那里的数据就没有了。编译器可没空帮你清空数据。在那个空间被下个使用者覆盖掉之前,如果你拿着原来的指针去访问,仍然可以获得原来的数据。
wevsty
2021-12-23 22:10:21 +08:00
不要混用普通指针和智能指针 p413
这个只是一个建议,并不是一个不可打破的规则。混用本身没有问题,只是你得知道自己在做什么。

shared_ptr 是内部帮你维护了一个引用计数, 通过引用计数来确保内存不再被引用的时候就释放。而在使用过程中如果使用 get() 或者直接使用原始指针并不改变引用计数,这是需要自己注意的。
yianing
2021-12-24 01:02:43 +08:00
内存被释放了不代表里面内容会被立即清除,继续访问属于未定义行为
aneostart173
2021-12-24 09:46:42 +08:00
你试试看*p3 = 0 会不会出错咯,或者 delete p3 会不会报 double free error.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/824078

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX