如何通过使用自定义构造函数而不调用析构函数来创建具有初始大小的向量? [复制]

Posted

技术标签:

【中文标题】如何通过使用自定义构造函数而不调用析构函数来创建具有初始大小的向量? [复制]【英文标题】:How to create a vector with an initial size by using a custom constructor and without calling the destructor? [duplicate] 【发布时间】:2018-11-11 13:33:10 【问题描述】:

举个例子:

#include <iostream>
#include <stdio.h>
#include <vector>

using namespace std;

class Foo
    public:
        Foo()
            cout << "Constructing the default way..." << endl;
            this->num = (int*)malloc(sizeof(int));
        
        Foo(int a)
            cout << "Using a custom constructor..." << endl;
            this->num = (int*)malloc(sizeof(int) * a);
        

        ~Foo()
            cout << "Destructing..." << endl;
            free((void*)num);
        

        int* num;
;

int main()

    vector<Foo> objects(5);
    for(Foo& v : objects) printf("%x0x\n", v.num);

    cout << "\n---------------\n";
    cout << "END OF PROGRAM!\n";
    cout << "---------------\n\n";
    return 0;

通过传递对象的初始计数来创建向量,从而单独创建每个对象,因此,所有对象的num 位于不同的地址,并且它们的析构函数在程序结束时被调用。

但是,如果我想通过给它一个自定义构造函数来创建向量,比如vector&lt;Foo&gt; objects(5, Foo(5)); (这只是我的猜测),一个临时对象被构造,然后复制到数组中的每个对象.实际上,这使得向量中的每个对象都有它们的num 指针指向相同的内存。另外,当初始化完成时,内存会被释放,然后每个对象都变得无用。

如何在不将自定义构造函数变成新的Init(int) 函数并在向量中的每个对象上运行它的情况下解决这个问题? (当然是把malloc改成realloc)。

【问题讨论】:

你可能想要std::fill。 这个问题展示了你想如何解决一个问题。问题的问题是您没有告诉我们您要解决的实际问题,您只是问我们如何解决这个未知问题的解决方案。这是一个非常典型的XY problem。请尝试向我们询问您想要解决的真正问题,告诉我们您的解决方案(您拥有的),也许我们将能够以更好的方式帮助您解决真正的问题。如果没有,那么我们将为您提供解决方案。 另外,在 C++ 中永远不要使用 mallocfree,甚至对于像 int 数组这样的原始类型也不行。始终以std::vector 开头。你需要了解the rules of three/five/zero,使用std::vector,你可以遵循我推荐的零规则。 malloc/free 绝不应该在 C++ 程序中使用。他们不调用构造函数/析构函数。改用new/delete - 好吧,不是真的;请使用容器和/或智能指针而不是手动内存管理。 这只是我的猜测 -- 无需猜测。通过打开 #include &lt;vector&gt; 标头,您可以轻松获得编译器的 std::vector 实现。或者简单地调试并逐步查看该构造函数是如何工作的。无论如何,std::vector 类很可能使用placement-new,而不是您认为的天真的“创建临时副本”。编写std::vector 实现的程序员是世界上最优秀的专业人士——他们对优化这些事情的了解远远超过普通 C++ 程序员。 【参考方案1】:

可以使用 Foo 对象的 unique_ptr 向量解决您的问题。

class Foo

public:
    Foo(int i) 

    // ...
;

#include <algorithm>
#include <vector>

auto main() -> int

    std::vector<std::unique_ptr<Foo>> foos(100);
    std::generate(foos.begin(), foos.end(), []()  return std::make_unique<Foo>(5); );

但是,您不想这样做,否则在调用向量的 Foo 对象上的任何内容时,您将不得不使用 unique_ptr 进行额外的间接处理。

正如其他人已经建议的那样,您应该将 Foo 的 int 成员存储在向量中。然后你可以只reserve向量中的空间和emplace_back(构造)你的Foo对象直接在Foo的向量中。

#include <vector>

class Foo

public:
    Foo() 
    Foo(int i) : ints(i) 

private:
    std::vector<int> ints;
;

namespace 

    constexpr auto NumberOfFoos = 100;


auto main() -> int

    std::vector<Foo> foos;
    foos.reserve(NumberOfFoos);

    for (auto i = 0; i < NumberOfFoos; ++i) 
        foos.emplace_back(10);
    

关于您的代码,您可能想看看这些额外的来源和答案(正如其他 cmets 已经建议的那样):

Rule of three Stop using std::endl Why is “using namespace std” considered bad practice?

【讨论】:

【参考方案2】:

有两个问题无法解决。

1) 您需要实现自定义复制和赋值运算符来进行深度复制,因为默认的浅复制(正如您指出的那样)没有做正确的事情。

2) 您需要让对象记住您为复制/分配实现工作分配的数组大小。

【讨论】:

以上是关于如何通过使用自定义构造函数而不调用析构函数来创建具有初始大小的向量? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

构造函数和析构函数

构造函数和析构函数

第四十六课继承中的构造与析构

php面向对象构造函数,析构函数

php构造函数的PHP 5 构造函数和析构函数

9——对象的创建和撤销,构造函数和析构函数