C ++ 11强制无条件地移动吗?

时间:2016-12-01 09:48:18

标签: c++ c++11 gcc

我使用命令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());
}

这是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不应强制执行move std::vector.push_back()。海湾合作委员会如何表现?

  3. 析构函数~s()被调用两次:首先将临时s()作为参数传递给ss.push_back(s()); 并移动,然后第二次std::vector<s>的析构函数被称为。

  4. 由于此代码不符合rule of three,因此在调用std::vector<s>的析构函数时注定会崩溃。对象ps指向的内容已被第一个~s()删除。因此,调用std::vector<s>的{​​{1}}析构函数必须崩溃,并显示~s()之类的错误。
  5. 然而,令我惊讶的是,这个程序以某种方式运行并正常终止。

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

    问题2 :根据double free or corruption日志,是否意味着为先前的C ++ 11会议设计的代码gdb但未满足rule of five,例如这个例子,当它们被编译成C ++ 11可执行文件时很可能会崩溃吗?

    修改

    由于我误解了rule of three这样的消息,提出了这个问题:

    gdb

    这让我认为GCC创建了一个移动构造函数。我只是按照这些专家告诉我的那样做了 - 在std::vector<s, std::allocator<s> >::push_back(s&&) emplace_back(std::move(__x)); std::vector<s, std::allocator<s> >::emplace_back<s>(s&&) 中设置一个断点,并注意到程序确实停在那里。这一发现使我的所有问题无效。

    正如这里的每位专家所说,事实是:  1. GCC不会创建任何移动构造函数。  2.调用现有的复制构造函数。

    谢谢大家的帮助!

3 个答案:

答案 0 :(得分:8)

  

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

它可能会这样做,作为复制省略优化。

  

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

没有。不生成移动构造函数。

  

我的问题是,类s显然有一个用户声明的析构函数~s(),这意味着类s不符合第四个条件

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

  

GCC如何表现?

海湾合作委员会似乎表现得如此。没有移动建设或任务。

  

析构函数~s()被调用两次

在您显示的gdb日志中,我只看到一个s::~s被调用的实例。

  

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

我会认为你不走运。似乎push_back没有使用复制赋值运算符,因此没有发生双重释放的条件。

  

问题2:根据gdb日志,它是否意味着为先前的C ++ 11设计的代码符合三条规则而不符合五条规则,如此示例...

此示例不符合三条规则,因此这似乎不是您要求的示例。

  

在编译为C ++ 11可执行文件时很可能会崩溃吗?

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

答案 1 :(得分: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:为什么程序不会崩溃?我只是幸运吗?

没有双重合作。

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

答案 2 :(得分:3)

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

复制构造函数是最佳候选者,用于复制临时生成的元素(调用默认和复制构造函数)。

这也为您提供exit