2016-12-01 63 views
7

我编译使用命令g++ -std=c++11 t.cpp下面的代码:C++ 11强制无条件移动吗?

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

这是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 

从日志,我在下面的印象:

  1. GCC忽略副本构造函数s(const s &other)
  2. GCC会自动创建一个move构造函数class s然后std::vector.push_back()调用move构造。

    This article状态

    因此,如果C类的定义不明确 声明的举动赋值运算符,一会隐含 声明为默认只有当所有的下列条件 满足:

    我这里的问题是,class s显然有一个用户声明的析构函数~s(),这意味着class s不符合规定的条件,因此GCC不应std::vector.push_back()执行move。 GCC如何表现?

  3. 析构函数~s()被称为两次:第一次临时立即之后s()已作为参数传给了ss.push_back(s());和移动,二是std::vector<s>析构函数被调用后。

  4. 由于此代码不符合rule of three,因此在调用std::vector<s>的析构函数时注定会崩溃。对象sp指向的内容已被第一个~s()删除。因此,std::vector<s>析构函数调用~s()必须崩溃,如double free or corruption

但是,让我吃惊的是,这个程序以某种方式运行并正常终止。

问题1:为什么程序不会崩溃?我真幸运吗?

问题2:根据gdb日志,岂不是专为之前C++ 11次会议rule of three但不符合rule of five,像这样的例子代码,很可能崩溃时被编译成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&&) 

导致我认为GCC创建一个移动构造函数。我只是做了很多这些专家告诉我的 - 在s(const s &other)中设置了一个断点,并且注意到程序确实停在那里。这一发现使我的所有问题都无效。

由于每个专家建议在这里,事实是: 1. GCC不会产生任何移动构造函数。 2.调用现有的拷贝构造函数。

谢谢大家的帮助!

+3

尝试在复制构造函数中放置断点。程序是否停在那个断点? –

+4

这'push_back'内部调用了一个名为',以初始化新元素的std :: move'功能,但这种初始化使用拷贝构造函数(因为没有移动构造函数和拷贝构造函数是最好的搭配)。没有移动构造函数被生成或调用。 – cpplearner

+0

@Someprogrammerdude我刚刚做到了。是的,程序停止在复制构造函数中。 –

回答

3

嗯,看看这个:

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

对于我来说,我收到破坏2个不同的对象:
0x7fffc879aa50默认构造函数
0x1668c40拷贝构造函数
0x7fffc879aa50析构函数
0x1668c40析构函数

问题1:为什么程序不会崩溃?我真幸运吗?

没有双重释放coraption的。

编译: 克++ -O3 --std = C++ 11 file2.cpp -o file2.out
G ++ 4.7.3

+0

消息'0x1668c40 copy constructor'成功消除了我的担忧,尽管我认为这两个'析构函数'消息不能证明两个'p'指向的两个内容是不同的。 –

3

如果复制构造明确声明(如在你的情况下)编译器不会自动生成移动构造函数或赋值构造函数。

复制构造是最佳候选,并用于临时生成的元素复制(包括默认和拷贝构造被调用)。

这也给你strong exception safety guarantee

+0

你是绝对正确的。非常感谢! –

8

GCC忽略复制构造函数s(const s & other)。

它可能做到这一点,作为拷贝省音优化。

GCC自动为S类创建一个移动构造函数,然后的std :: vector.push_back()调用移动的构造。

号,不产生转移构造函数。

我这里的问题是,S类显然有一个用户声明的析构函数〜S(),这意味着S类不符合规定条件

正确的。该类也不符合第一个条件(用户声明的拷贝构造函数)。

如何GCC的行为呢?

GCC似乎表现为它应该。没有涉及任何移动建设或任务。

析构函数〜S()被调用两次

在你显示GDB日志,我只看到s::~s一个实例被调用。

问题1:为什么程序不会崩溃?我真幸运吗?

我会认为你不吉利。看起来push_back没有使用复制赋值操作符,所以没有发生double free条件。

问题2:根据GDB日志,岂不是专为三之前C++ 11日的会议规则代码,但不能满足的5种规则,这样的例子......

这例子不符合三的规则,所以这似乎不是你要求的例子。

当它们被编译为C++ 11可执行文件时很可能会崩溃?

不需要。只要代码遵循三条规则,则复制对象是安全的。遵循五项规则只是为了使对象可移动(以避免复制)。

+0

谢谢!事实上,这个示例代码不符合“三条规则”。为了便于阅读,我省略了“复制赋值运算符”,以尽可能缩短代码的长度,并希望获得更多帮助。显然我做得太短了。 –