c++ - 使用 push_back 函数调用复制构造函数的 vector 内存分配调用

标签 c++ c++11 constructor

<分区>

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

从上面的代码我有2个疑惑:

  1. 当我使用 2 个 push_back(mc) 函数时,为什么没有从已实现的类中调用 move ctor 复制 ctor 的调用是 3 次,即第一次推送 1 次,第二次推送第一个 vector 被调整大小(随后增长)到不同的内存位置(第一次推送应该触发移动)第二次推送第三次

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

输出:

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

我使用的 gcc 版本是:

> gcc version 4.7.3

更新

感谢您的回复,我正在做某事

对于我的 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;
}

我已经编辑了上面的代码

//      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..

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

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

Internal compiler error: Error reporting routines re-entered.

Please submit a full bug report, with preprocessed source if appropriate.

See for instructions. Preprocessed source stored into /tmp/ccHhV599.out file, please attach this to your bugreport.

为什么它给出这个错误,为什么它不使用它的默认复制构造函数

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

for(it = vecSource.begin(); it != vecSource.end(); ++it )
    cout << (*it).fun() << endl;

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

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

补充一下,我认为这里存在调整保留大小的差异,其中保留不调用默认 ctor 并使内存未初始化。

我认为最好在我们需要的范围内为 vector 保留一些空间,而不是避免定期内存交换。即使超过它也会到新的位置。

更新

代码修改

   vector<MoveableClass> vecSource;

    vecSource.push_back(mc);
    vecSource.push_back(mc);

我得到的o/p是

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

我对这里的顺序感到困惑。 我很期待 复制构造函数 移动构造函数 拷贝构造函数

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

问候!

最佳答案

vector is resized (sequently grow) to different memory location (which should have triggered move for first push)

std::vector仅当使用 noexcept specifier 声明该移动构造函数时,才会在重新分配期间使用该移动构造函数,或者如果没有可用的复制构造函数(请参阅 std::move_if_noexcept 了解更多详细信息):

通过添加以下小改动:

MoveableClass(MoveableClass&& src) noexcept {
//                                 ~~~~~~~^
        cout << "Move constructor" << endl;
}

你会得到输出:

Copy constructor
Move constructor
Move constructor
Move constructor

noexcept说明符告诉 std::vector它可以安全地将移动语义应用于其内容的实现。否则,您将没有强大的异常安全保证,它基本上声明如果由于异常导致重新分配失败, vector 将保持完整:

§23.3.6.5 [vector.modifiers]/p1:

Requires: If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects. If an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.

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

vecSource.push_back(std::move(mc));
//                  ~~~~~~~~^

输出:

Move constructor

Even when I initialize the vector object with size 3 why the call to copy ctor increases to 4 for one push_back(mc).

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

可以通过在 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

输出:

Default constructor
Default constructor
Default constructor
----
Default constructor
Copy constructor

2) point when I initilize for size 3 , does this mean 3 memory locations are initialized with class instance ?

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


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

I am not able to use above code It gives error

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

for(auto it = vecSource.begin(); it != vecSource.end(); ++it )
    (*it).fun();

If I comment copy constructor I am getting compile error

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

vecSource.push_back(mc);
//...
vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());

"Copy constructor Copy constructor Move constructor." I am confused by the order here. I am expecting it "Copy constructor Move constructor Copy constructor"

对于下面的一段代码:

vector<MoveableClass> vecSource;
vecSource.push_back(mc);
vecSource.push_back(mc);

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

  1. 初始容量vector设置为 0 .
  2. 第一个push_back调用:mc复制插入到新分配的内存存储(复制构造函数)。
  3. 第二 push_back调用:mc试图复制插入。 vector的容量|太小了,所以分配了一个新的存储空间。 mc的拷贝被插入到新存储中(复制构造函数)。然后,将其余元素移动到新的内存位置(移动构造函数)。

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


旁注:

  1. 首选 -std=c++11-std=c++0x如果编译器支持前者。

  2. 您不应重复使用已移出的实例。我希望您这样做只是为了测试目的。

关于c++ - 使用 push_back 函数调用复制构造函数的 vector 内存分配调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27677294/

相关文章:

c++ - enable_shared_from_this 和继承

c++ - 编译器何时应该生成 move 构造函数?

java - 初始化表达式可以像 C++ 一样用于 Java 中的构造函数吗?

c++ - 构造函数/函数声明参数列表中的统一初始化

c++ - 从 boost python 导入依赖的 python 库

c++ - 传递字符串指针然后在构造函数/方法中检查大小的问题

c++ - GDB 以无序的方式跳到错误的行

c++ -/usr/bin/ld : cannot find -ldlib/usr/bin/ld: cannot find -lcblas/usr/bin/ld: cannot find -llapack

C++ 在 .size() 之后丢失 vector 元素

Scala:扩展参数化类时处理不同的构造函数