作者回复: 3的正解终于出现,有人说到“异常安全”了。👍
再说两句,这是C++98时设计的接口,没有移动就只能那样。有了移动,在多线程的环境里,移动返回加弹出实际上就变得有用了。我对复杂和可读性部分不那么同意。
作者回复: 很棒👌。
异常安全是关键。
作者回复: catch住也没有用了。仔细想一下,我现在要把vector里的两个对象移到一个新的vector,移第一个成功,第二个时有异常,然后vector该怎么办?现在两个vector都废掉了。
拷贝不影响旧的容器。即使发生异常,至少老的那个还是好的。这就是异常安全。
作者回复: 挺好。三比其他回答已经进一步了,但还是没有触及到某个关键字。
作者回复: 关于几次拷贝/移动的问题?参考 hello world 的评论下的廖熊猫的回答:
在插入的时候,你会发现空间不够了,然后开辟新的空间,在新空间先把最后插入的元素放好,然后再依次把以前的元素一个一个挪过来。空间不够的话最后一个元素是没法插入进去的啊,所以没办法移动三次的。
还有我自己的回答:
两者都是要构造第 3 个对象时空间不足,需要这样:
1. 分配一个足够大的新内存区域。
2. 在上面构造第 3 个对象。
3. 如果成功(没有异常),再移动/拷贝旧的对象。
4. 全部成功,则析构旧对象,释放旧对象的内存。
5. 如果 1 出现异常,直接抛出即可;如果 2–3 出现异常,则析构已成功构造的对象,释放新内存空间,继续抛出异常。
如果不是这个问题。请把问题阐释得更详细些。可以重新开一个新的评论。
作者回复: 学习很认真,回答也基本抓住要点了,尤其问题 2 和 3。👍
作者回复: 头两个在已有空间上成功构造。第三个时发现空间不足,系统会请求更大的空间,大小由实现决定(比如两倍)。有了足够的空间后,就会在新空间的第三个的位置构造(第三个obj1),成功之后再把头两个拷贝或移动过来。
作者回复: 只有const char*,目标是const string&,会引发一个临时string的构造,会导致内存复制。
用string_view当然也会产生临时对象,但string_view不会复制字符串的内容。
作者回复: 这个疑问提得好👍。在目前这个例子里,确实是移动构造而不是拷贝构造。
作者回复: 会将前面的元素拷贝或移动一遍。
移动的条件文中提到了,元素类型需要“提供一个保证不抛异常的移动构造函数”。
作者回复: 多谢多谢。已经修复。
作者回复: https://www.cnblogs.com/skyfsm/p/7488053.html
这篇中文文章说得还比较清楚,可以看一下。
作者回复: 不是。执行 v1.reserve(2) 之后,空间大小就是 2 了。扩充空间是一个编译器自发进行的操作,没有用户控制。一般会类似于 reserve(size() * 2)。
作者回复: 对,缺省情况下,std::terminate 会被调用。
作者回复: 就像你在代码里对 str 定义两次一样啊。这仍然是 C++,不是 Python。
Restart kernel 可以重新来。
作者回复: Java是引用语义,返回对象就是返回个指针,没有任何问题。
C++是值语义,以前返回对象只能是拷贝,可能发生异常。一旦发生异常,对象已经被弹出,那它就彻底“丢失”了。
作者回复: 指单个元素的大小,sizeof。
作者回复: 上来就错了。大部分容器都是在堆上分配空间的……
2 正确。内存分配成功,新对象构造成功,才移动/拷贝旧对象。
作者回复: 可以。够言简意赅的。
作者回复: 还是重点谈3。对于C++的情况,基本没问题。对于Java,则错了。Java的情况最接近于你返回一个智能指针——这个操作本身性能是没问题的。主要约束是必须在堆上放置对象。
2 你对防犯错的考虑非常好。其他人似乎没提到。👍