std::vector 范围初始化的幕后是什么? [重复]

问题描述 投票:0回答:2
#include <utility>
#include <vector>
#include "iostream"

class Person {
public:
    std::string name{"no-name"};

    Person() {
        std::cout << std::string("Person()") << std::endl;
    }

    explicit Person(std::string name) : name(std::move(name)) {
        std::cout << std::string("Person(") + this->name + ")" << std::endl;
    }

    // operator =
    Person &operator=(const Person &other) {
        std::cout << std::string("operator= ") + other.name + "->" + this->name + "" << std::endl;
        return *this;
    }

    Person(const Person &other) noexcept {
        std::cout << std::string("copy ") + other.name + "->" + this->name + "" << std::endl;
        this->name = other.name;
    }

    // move
    Person(Person &&other) noexcept {
        std::cout << std::string("move ") + other.name + "->" + this->name + "" << std::endl;
        this->name = std::move(other.name);
    }

    ~Person() {
        std::cout << std::string("~Person(") + name + ")" << std::endl;
    }

};

int main() {

    std::cout << "construct person_list" << std::endl;
    std::vector<Person> person_list (4);

    std::cout << "construct person_list_copy" << std::endl;
    std::vector<Person> person_list_copy (person_list.begin(), person_list.end());
}

输出:

construct person_list
Person()
Person()
Person()
Person()
construct person_list_copy
copy no-name->no-name
copy no-name->no-name
copy no-name->no-name
copy no-name->no-name
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)

问题

在复制操作发生之前向量“person_list_copy”的足够空间来自哪里??

我认为向量很像一个c风格的固定大小数组,我们首先需要有足够的空间,我认为应该调用默认构造函数,然后我们将一些对象分配到那些预先构造的“单元格”中,我认为应该调用复制构造函数。

上面的代码直接用复制构造函数创建了对象,但是怎么可能在数组空间构造完成之前就将对象放入数组中???

我以前在 C 编程方面从未见过这样的事情。而且我无法找到一种可用的方法来实现 std::vector 所用的所有术语,例如“new”、uint8_tfixed_array[LEN]、malloc。

我是一个C++初学者,我脑子里一定对向量范围初始化有一些误解,或者可能有一些关于cpp和STL我还不知道的魔法。

如果有人指出我遇到的问题,我将不胜感激。非常感谢。

预期产量

construct person_list
Person()
Person()
Person()
Person()
construct person_list_copy
Person()
Person()
Person()
Person()
copy no-name->no-name
copy no-name->no-name
copy no-name->no-name
copy no-name->no-name
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)
~Person(no-name)
c++ stl stdvector allocation
2个回答
0
投票

嗯,这就是 C++ 可以做的,并且如果可能的话也会做的。首先分配空内存,然后调用构造函数。 std 类是特意编写的,目的是为了节省 CPU 时间。

在 C++ 中,你可以完全控制原始内存,并且可以选择随心所欲地使用它。包括诸如未初始化或在任意内存块上强制调用初始化之类的内容。

请参阅 https://www.geeksforgeeks.org/placement-new-operator-cpp/,了解如何在后台完成此操作的示例。


0
投票

std::vector
如何构造新元素取决于模板参数
Allocator
std::vector<T, Allocator>
调用
Allocator::allocate
来保留内存,并调用
Allocator::construct
(C++20 之前)或
std::allocator_traits<Allocator>::construct
(C++20 以来)来实际构造元素。这些函数采用一个地址来构造对象以及要转发到实际构造函数的参数,并且应该就地构造元素。

内存分配和元素构造是两个独立的步骤,不需要先默认构造元素。如果元素必须首先默认构造,则不可能将

std::vector
用于非默认构造类型,但使用
std::vector<S>
是完全有效的,其中
S
不是默认构造类型。事实上,对
element_type
使用的
std::vector
的要求取决于要调用的实际函数,但最低限度是该元素是 Erasable。默认可构造是比可擦除更严格的条件,并且不是必需的。

Allocator
的默认参数是
std::allocator
。对于
std::allocator
Allocator::construct
(C++20 之前)和
std::allocator_traits<Allocator>::construct
(C++20 之后,通过调用
std::construct_at
)最终都会调用 placement new 在已分配的内存中构造对象。如果您使用自定义
Allocator
模板参数,则可能会在构建实际元素之前执行默认构建,但这样做并不常见,而且效率也不高。

© www.soinside.com 2019 - 2024. All rights reserved.