【问题标题】:std::vector push_back and class constructor not being called?std::vector push_back 和类构造函数没有被调用?
【发布时间】:2012-06-01 15:49:35
【问题描述】:

我有这样的课

class variable
{
    public:
        variable(int _type=0) : type(_type), value(NULL), on_pop(NULL)
        {
        }

        virtual ~variable()
        {
            if (type)
            {
                std::cout << "Variable Deleted" <<std::endl;
                on_pop(*this);
                value=NULL;
            }
        }

        int     type;
        void*   value;
        typedef void(*func1)(variable&);
        func1 on_pop;
}

然后我将实例推送到 std::vector 中,如下所示:

stack.push_back(variable(0));

我希望变量的析构函数会被调用,但是 if 直到一个值被分配给 type 才会进入,因为我希望我提供的构造函数会在实例被复制到向量中时被调用。但由于某种原因,它不是。

调用 stack.push_back 后,析构函数(副本的?)运行,并且类型有一些随机值,就像从未调用过构造函数一样。

我似乎无法弄清楚我做错了什么。请帮忙! ^_^

编辑:

好的,这是一个独立的例子来说明我的意思:

#include <iostream>
#include <vector>

class variable
{
    public:
        variable(int _type=0) : type(_type), value(NULL), on_pop(NULL)
        {
        }

        ~variable()
        {
            if (type)
            {
                std::cout << "Variable Deleted" <<std::endl;
                on_pop(*this);
                value=NULL;
            }
        }

        int     type;
        void*   value;

        typedef void(*func1)(variable&);
        func1 on_pop;
};

static void pop_int(variable& var)
{
    delete (int*)var.value;
}

static void push_int(variable& var)
{
    var.type = 1;
    var.value = new int;
    var.on_pop = &pop_int;
}

typedef void(*func1)(variable&);
func1 push = &push_int;

int main()
{
    std::vector<variable>   stack;

    stack.push_back(variable(0));
    push(stack[stack.size()-1]);

    stack.push_back(variable(0));
    push(stack[stack.size()-1]);

    stack.push_back(variable(0));
    push(stack[stack.size()-1]);

    return 0;
}

上面的程序输出如下:

Variable Deleted
Variable Deleted
Variable Deleted
Variable Deleted
Variable Deleted
Variable Deleted

Process returned 0 (0x0)   execution time : 0.602 s
Press any key to continue.

【问题讨论】:

  • 不清楚实际发生了什么,以及您期望发生什么:即使允许编译器在这里优化,它正确初始化所有对象,所以这段代码可以正常工作正如我所期望的那样。显然,您的期望不同,但我不明白它们是什么,或者为什么。
  • @KonradRudolph 我添加了一个 SSCCE 来展示我正在尝试做的事情。如果析构函数被调用 6 次,我希望构造函数被调用相同的次数。

标签: c++ vector


【解决方案1】:

欢迎来到 RVO 和 NRVO。这基本上意味着编译器可以跳过创建一个多余的对象——即使它的构造函数和析构函数有副作用。您不能依赖立即复制或移动到实际存在的对象。

编辑:vector 中的实际值根本无法省略。只能省略中间变量variable(0)vector 中的对象仍必须照常构造和销毁。这些规则仅适用于临时工。

编辑:你为什么要编写自己的资源管理类?您可以简单地将unique_ptr 与自定义删除器一起使用。还有你自己的 RTTI?

每个被破坏的对象必须被构造。标准中没有违反这一点的规则。 RVO 和 NRVO 仅在您开始时才会出现问题,例如,在构造函数/析构函数中修改全局变量。否则,它们对程序的正确性没有影响。这就是为什么他们是标准的。你一定是做错了什么。

最终,我只是不确定 WTF 到底发生在您身上,以及为什么它不工作或“工作”应该是什么。发布 SSCCE。

编辑:根据您的 SSCCE,绝对没有出了什么问题。这完全是预期的行为。您没有遵守三原则——也就是说,您在析构函数中销毁了资源,但没有努力确保您确实拥有相关资源。您的编译器生成的复制构造函数正在破坏您的逻辑。您必须阅读关于 C++ 中资源处理的三法则、复制和交换以及类似的习惯用法,并且最好使用已经作为标准提供的智能指针,例如 unique_ptr,它没有这些问题。

毕竟,您创建了variable 的六个实例——三个临时对象在堆栈上,三个在向量内部。所有这些都调用了它们的析构函数。问题是您从未考虑过复制操作或复制会做什么或这些临时对象会发生什么(提示:它们会被破坏)。

考虑同样的例子

int main()
{
    variable v(0);
    push_int(v);
    variable v2 = v;
    return 0;
}

变量v被构造并分配了一个新的int,一切都很好。但是等等-然后我们将其复制到v2。编译器生成的构造函数复制所有位。然后v2v 都被销毁了——但它们都指向同一个资源,因为它们都持有同一个指针。双重删除比比皆是。

您必须定义复制(共享所有权 - std::shared_ptr)或移动(唯一所有权 - std::unique_ptr)语义。

编辑:只是一个简短的说明。我观察到您实际上不会push 进入项目,直到它们已经在向量中。但是,当添加其他元素时必须调整矢量大小时,观察到相同的效果,并且根本原因是相同的。

析构函数被调用了 6 次。 A 构造函数被调用六次。只是不是你想要的。

【讨论】:

  • 我确信这是正确的答案,但我不明白如何:constructor 不能被省略,只有 copy。因此,type 应正确初始化为 0,并且临时的析构函数(如果存在)应该执行if 块。
  • 当我将对象插入到 std::vector 时,我应该如何实现这个简单的愿望,即正确初始化对象? ^_^
  • @damian:会的。只是没有复制。 target 对象仍然必须被构造和破坏,它不会被复制。因此,最好让一个管理一个资源的类在构造函数中获取它并在析构函数中销毁它。
  • 所以如果我需要有条件地在对象析构函数中运行一些代码,当我将它插入到 std::vector 时我不能这样做?有没有什么方法可以在不必编写自己的容器的情况下实现这一点?
  • @damian:不,它不是这样工作的。像unique_ptrshared_ptr 这样的类必须一直做资源管理逻辑,这没有问题。
【解决方案2】:

好的。我一直在阅读更多关于不同容器内在特性的内容,显然,我在这里尝试完成的工作是 std::deque

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-06
    • 1970-01-01
    • 1970-01-01
    • 2021-05-05
    • 1970-01-01
    • 2020-10-05
    • 2018-12-05
    相关资源
    最近更新 更多