向量内存分配调用复制构造函数与push_back函数

kni*_*ils 0 c++ constructor c++11

#include <vector>
#include <iostream>
#include <iterator>

using namespace std;

class MoveableClass
{
public:
        MoveableClass() {
                cout << "Default constructor" << endl;
        }
        MoveableClass(const MoveableClass& src) {
                cout << "Copy constructor" << endl;
        }
        MoveableClass(MoveableClass&& src) {
                cout << "Move constructor" << endl;
        }
        MoveableClass& operator=(const MoveableClass& rhs) {
                cout << "Copy assignment operator" << endl;
                return *this;
        }
        MoveableClass& operator=(MoveableClass&& rhs) {
                cout << "Move assignment operator" << endl;
                return *this;
        }
};

int main()
{
    vector<MoveableClass> vecSource(3);
    cout << "----" << endl;
    MoveableClass mc;
    cout << "----" << endl;
    vecSource.push_back(mc);
//      vecSource.push_back(mc);
//      vecSource.push_back(mc);
//      vecSource.push_back(mc);
    cout << "----" << endl;
    // Copy the elements from vecSource to vecOne
    vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
    cout << "----" << endl;
    // Move the elements from vecSource to vecTwo
    vector<MoveableClass> vecTwo(make_move_iterator(vecSource.begin()),
                                                         make_move_iterator(vecSource.end()));
    cout << "----" << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

从上面的代码我有两个疑问:

  1. 当我使用2个push_back(mc)函数调用复制ctor时,为什么移动ctor不会从实现类调用3次,即第一次按下1次,第二次推送第一次向量调整大小(后续增长)到不同的内存位置(应该有触发第一次推动的动作)第二次推动第3次

  2. 即使我初始化大小为3的向量对象,为什么对于一个push_back(mc),复制ctor的调用增加到4.

输出:

Default constructor
Default constructor
Default constructor
----
Default constructor
----
Copy constructor
Copy constructor
Copy constructor
Copy constructor
----
Copy constructor
Copy constructor
Copy constructor
Copy constructor
----
Move constructor
Move constructor
Move constructor
Move constructor
----
Run Code Online (Sandbox Code Playgroud)

我正在使用的gcc版本是:

> gcc version 4.7.3
Run Code Online (Sandbox Code Playgroud)

UPDATE

谢谢你回复我到了某个地方

为了我的1)我想补充一点

//    MoveableClass(const MoveableClass& src) {
//                        cout << "Copy constructor" << endl;
//                }

    MoveableClass(MoveableClass&& src) noexcept {
                    cout << "Move constructor" << endl;
            }
    ....
    void fun() {
                    cout << "hello\n";
            }

    int main()
    {
            vector<MoveableClass> vecSource(3);
    //        vector<MoveableClass>::iterator it;
       //     vecSource.reserve(3);
            cout << "----" << endl;
            MoveableClass mc;
            cout << "----" << endl;
            mc.fun();
            vecSource.push_back(mc);
    //      vecSource.push_back(move(mc));
    //      vecSource.push_back(move_if_noexcept(mc));
    //      vecSource.push_back(mc);
    //      vecSource.push_back(mc);
    //      vecSource.push_back(mc);
    //        for(it = vecSource.begin(); it != vecSource.end(); ++it )
     //          cout << (*it).fun() << endl;
            cout << "----" << endl;
            // Copy the elements from vecSource to vecOne
            vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
        cout << "----" << endl;
        // Move the elements from vecSource to vecTwo
        vector<MoveableClass> vecTwo(make_move_iterator(vecSource.begin()),
                                                             make_move_iterator(vecSource.end()));
        cout << "----" << endl;

        return 0;
}
Run Code Online (Sandbox Code Playgroud)

我编辑了以上代码

//      vecSource.push_back(move(mc));               I can call move ctor only
//      vecSource.push_back(move_if_noexcept(mc));  I can call move ctor only
                 understood..
Run Code Online (Sandbox Code Playgroud)

如果我评论复制构造函数我得到编译错误

knils @ knils-HP:IteratorAdapters $ g ++ -g -std = c ++ 0x MoveIterators.cpp

内部编译器错误:重新输入错误报告例程.

如果合适,请提交完整的错误报告,并提供预处理的来源.

请参阅说明.存储在/tmp/ccHhV599.out文件中的预处理源,请将其附加到您的bug报告中.

为什么它给出了这个错误,为什么它不使用它的默认拷贝ctor

2)当我初始化为3时,这是否意味着3个内存位置是用类实例初始化的?

for(it = vecSource.begin(); it != vecSource.end(); ++it )
    cout << (*it).fun() << endl;
Run Code Online (Sandbox Code Playgroud)

我无法使用上面的代码它给出了错误

MoveIterators.cpp:48:30: note: mismatched types ‘const _CharT*’ and ‘void’

要添加我认为这里有一个差异,用于调整保留不会调用默认ctor并使内存未初始化的保留.

我认为最好在我们需要的范围内使用保留到某个空间的向量,这样可以避免常规的内存交换.即使它超过它将到新的位置.

UPDATE

对于一段代码更改

   vector<MoveableClass> vecSource;

    vecSource.push_back(mc);
    vecSource.push_back(mc);
Run Code Online (Sandbox Code Playgroud)

我得到的o/p是

复制构造函数复制构造函数移动构造函数

我对此处的订单感到困惑.我期待它复制构造函数移动构造函数复制构造函数

因为第一次推送它初始化一个大小(副本)第二次它重新分配,所以将现有内存移动到新位置(移动)和复制第二次推送新位置(复制)编译器不同为什么..

问候!

Pio*_*cki 5

向量被调整大小(后续增长)到不同的内存位置(应该首先按下触发移动)

std::vector只有在使用noexcept说明符声明了move-constructor 或者没有可用的copy-constructor时,才会在重新分配期间使用move-constructor (std::move_if_noexcept有关详细信息,请参阅参考资料):

通过添加以下小改动:

MoveableClass(MoveableClass&& src) noexcept {
//                                 ~~~~~~~^
        cout << "Move constructor" << endl;
}
Run Code Online (Sandbox Code Playgroud)

你会得到输出:

Copy constructor
Move constructor
Move constructor
Move constructor
Run Code Online (Sandbox Code Playgroud)

noexcept说明符告诉std::vector它可以安全地应用实现移动语义其内容.否则,您将没有强大的异常安全保证,这基本上表明如果重新分配由于异常而导致向量保持不变:

§23.3.6.5[vector.modifiers]/p1:

要求:如果除了复制构造函数之外引发异常,则移动构造函数,赋值运算符或移动赋值运算符T或通过任何InputIterator运算都没有效果.如果非移动构造函数抛出异常,CopyInsertable T则不指定效果.

此外,push_back成员函数不会尝试移动构造一个新元素,除非它的参数可以被非const右值引用绑定 - 如果不是,那么它将回退到复制构造.如果要基于调用中的mc实例移动构造新元素push_back,则需要传入xvalue mc:

vecSource.push_back(std::move(mc));
//                  ~~~~~~~~^
Run Code Online (Sandbox Code Playgroud)

输出:

Move constructor
Run Code Online (Sandbox Code Playgroud)

即使我初始化大小为3的向量对象,为什么对于一个push_back(mc),复制ctor的调用增加到4.

vecSource可以使用.capacity()成员函数查询的初始容量可能3 在您的情况下设置,这意味着任何存储更多元素的尝试都会导致需要重新分配,这需要已经存储在向量中的所有元素被复制构造到新的存储位置.

通过在接下来push_back呼叫之前保留足够的存储空间,可以避免意外的重新分配:

vector<MoveableClass> vecSource;
vecSource.reserve(4);    // reserve a storage for 4 elements
vecSource.resize(3);     // default-construct 3 elements
cout << "----" << endl;
MoveableClass mc;
vecSource.push_back(mc); // copy-construct 4th element
Run Code Online (Sandbox Code Playgroud)

输出:

Default constructor
Default constructor
Default constructor
----
Default constructor
Copy constructor
Run Code Online (Sandbox Code Playgroud)

2)当我初始化为3时,这是否意味着3个内存位置是用类实例初始化的?

是的,通过在向量的构造函数调用中给出初始容量,或者通过使用resize成员函数,在C++ 11中,您获得了默认构造的数量(在C++ 03中 - 从默认构造的元素复制构造)准备好访问和使用的元素.


cout << (*it).fun() << endl;

我无法使用上面的代码它给出了错误

您无法打印出声明void为返回类型的函数调用的结果.只需删除该cout部分,它将编译:

for(auto it = vecSource.begin(); it != vecSource.end(); ++it )
    (*it).fun();
Run Code Online (Sandbox Code Playgroud)

如果我评论复制构造函数我得到编译错误

某些操作要求vector元素的类型为CopyConstructible ; 在你的代码中这些是:

vecSource.push_back(mc);
//...
vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
Run Code Online (Sandbox Code Playgroud)

"复制构造函数复制构造函数移动构造函数." 我对此处的订单感到困惑.我期待它"复制构造函数移动构造函数复制构造函数"

对于以下代码:

vector<MoveableClass> vecSource;
vecSource.push_back(mc);
vecSource.push_back(mc);
Run Code Online (Sandbox Code Playgroud)

根据您的输出,发生以下情况:

  1. 初始容量vector设置为0.
  2. 第一次push_back调用:将mc复制插入到新分配的内存存储(复制构造函数).
  3. 第二次push_back调用:mc尝试复制插入.容量vector太小,因此分配了新的存储空间.将副本mc插入到新存储(复制构造函数)中.然后,将其余元素移动到新的内存位置(Move构造函数).

我不认为附加元素的复制构造在标准强制重新定位之前的顺序,它只是在你正在使用的libstdc ++中实现它的方式.


附注:

  1. 宁愿-std=c++11-std=c++0x如果编译器支持前者.

  2. 您不应该重用已经移过的实例.我希望你这样做只是为了测试目的.