我使用命令编译以下代码g++ -std=c++11 t.cpp:
#include <vector>
#include <cstring> //memcpy()
class s
{
char *p;
size_t size;
public:
s(){
size=10;
p=new char[size];
}
s(const s &other){
size=other.size;
p=new char[size];
memcpy(p,other.p,other.size);
}
~s(){ delete [] p; }
};
int main()
{
std::vector<s> ss;
ss.push_back(s());
}
Run Code Online (Sandbox Code Playgroud)
这是gdb日志:
Breakpoint 1, main () at t.cpp:23
23 ss.push_back(s());
s::s (this=0x7fffffffe370) at t.cpp:9
9 size=10;
10 p=new char[size];
11 }
std::vector<s, std::allocator<s> >::push_back(s&&) (this=0x7fffffffe350,
__x=<unknown type in /tmp/a.out, CU 0x0, DIE 0x20d0>)
at /usr/include/c++/4.9/bits/stl_vector.h:932
932 { emplace_back(std::move(__x)); }
std::move<s&> (__t=...) at /usr/include/c++/4.9/bits/move.h:102
102 { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
std::vector<s, std::allocator<s> >::emplace_back<s>(s&&) (this=0x7fffffffe350)
at /usr/include/c++/4.9/bits/vector.tcc:94
94 if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
101 _M_emplace_back_aux(std::forward<_Args>(__args)...);
102 }
s::~s (this=0x7fffffffe370, __in_chrg=<optimized out>) at t.cpp:17
17 ~s(){ delete [] p; }
std::vector<s, std::allocator<s> >::~vector (this=0x7fffffffe350, __in_chrg=<optimized out>)
at /usr/include/c++/4.9/bits/stl_vector.h:425
Run Code Online (Sandbox Code Playgroud)
从日志中,我的印象如下:
s(const s &other).GCC自动move为其创建一个构造函数class s,然后std::vector.push_back()调用该move构造函数.
这篇文章指出
因此,如果类C的定义没有显式声明一个移动赋值运算符,只有满足以下所有条件时,才会隐式声明一个默认值:
在这里,我的问题是,class s显然有一个用户声明的析构函数~s(),意思class s不符合规定的条件,因此GCC不应该强制执行move的std::vector.push_back().GCC如何表现如此?
析构~s()函数被调用两次:首先将临时s()函数作为参数传递给ss.push_back(s()); 并移动,然后在std::vector<s>调用析构函数后第二次调用.
std::vector<s>调用析构函数时注定要崩溃.p对象指向的内容s已被第一个删除~s().因此,std::vector<s>析构函数调用~s()必须崩溃,如同错误double free or corruption.然而,令我惊讶的是,这个程序以某种方式运行并正常终止.
问题1:为什么程序不崩溃?我只是幸运吗?
问题2:根据gdb日志,是否意味着为先前的C++ 11会议rule of three而不是满足5的规则而设计的代码(如此示例)在编译为C++ 11可执行文件时很可能会崩溃?
编辑:
由于我对gdb这些消息的误解,提出了这个问题:
std::vector<s, std::allocator<s> >::push_back(s&&)
emplace_back(std::move(__x));
std::vector<s, std::allocator<s> >::emplace_back<s>(s&&)
Run Code Online (Sandbox Code Playgroud)
这让我认为GCC创建了一个移动构造函数.我只是做了很多这些专家告诉我的 - 设置一个断点s(const s &other),并注意到程序确实停在那里.这一发现使我的所有问题无效.
正如这里的每位专家所建议的那样,事实是:1.GCC不会创建任何移动构造函数.2.调用现有的复制构造函数.
谢谢大家的帮助!
GCC忽略了复制构造函数(const s&other).
它可能会这样做,作为复制省略优化.
GCC自动为类s创建移动构造函数,然后调用移动构造函数的std :: vector.push_back()调用.
不会.不会生成移动构造函数.
我的问题是类s显然有一个用户声明的析构函数~s(),这意味着类s不符合第四个条件
正确.该类也不符合第一个条件(用户声明的复制构造函数).
GCC如何表现如此?
海湾合作委员会似乎表现得如此.不涉及移动建设或任务.
析构函数~s()被调用两次
在您显示的gdb日志中,我只看到一个s::~s被调用的实例.
问题1:为什么程序不崩溃?我只是幸运吗?
我觉得你不走运.似乎push_back没有使用复制赋值运算符,因此没有发生双重释放的条件.
问题2:根据gdb日志,它是否意味着为先前的C++ 11设计的代码符合三条规则但不符合五条规则,如此示例...
此示例不符合三个规则,因此这似乎不是您要求的示例.
很可能在编译成C++ 11可执行文件时崩溃?
不可以.只要代码遵循三条规则,就可以安全地复制对象.遵循五的规则只是为了使对象可移动(避免复制).
嗯,检查一下:
#include <vector>
#include <cstring> //memcpy()
#include <iostream>
class s
{
char *p;
size_t size;
public:
s(){
std::cout<<this<<"\tdefault constructor\n";
size=10;
p=new char[size];
}
s(const s &other){
std::cout<<this<<"\tcopy constructor\n";
size=other.size;
p=new char[size];
memcpy(p,other.p,other.size);
}
~s(){
std::cout<<this<<"\tdestructor\n";
delete [] p;
}
};
int main()
{
std::vector<s> ss;
ss.push_back(s());
}
Run Code Online (Sandbox Code Playgroud)
对我来说,我正在破坏2个不同的对象:
0x7fffc879aa50 默认构造函数
0x1668c40 复制构造函数
0x7fffc879aa50 析构函数
0x1668c40 析构函数
问题1:为什么程序不崩溃?我只是幸运吗?
没有双重的免于同化。
编译:g++ -O3 --std=c++11 file2.cpp -o file2.out
G++ 4.7.3