使用智能指针在 C++ 中的直接无环图中连接父/子会导致 SIGSEGV

问题描述 投票:0回答:1

我正在尝试用 C++20 构建 DAG。每个节点都有多个子节点和父节点。作为 MWE,每个节点都可能计算一些值。

来自 Java 世界,这段代码可以按预期工作:

import java.util.ArrayList;
import java.util.List;

abstract class AbstractDAGNode {

    List<AbstractDAGNode> children = new ArrayList<>();
    List<AbstractDAGNode> parents = new ArrayList<>();

    public AbstractDAGNode(List<AbstractDAGNode> children) {
        this.children = children;
        for (AbstractDAGNode child : children) {
            child.parents.add(this);
        }
    }

    public abstract double value();
}

class ValueDAGNode extends AbstractDAGNode {
    double value = 0;

    public ValueDAGNode(double value) {
        super(new ArrayList<>());
        this.value = value;
    }

    @Override
    public double value() {
        return this.value;
    }
}

class SumDAGNode extends AbstractDAGNode {

    public SumDAGNode(List<AbstractDAGNode> children) {
        super(children);
    }

    @Override
    public double value() {
        double result = 0;
        for (AbstractDAGNode child : children) {
            result += child.value();
        }
        return result;
    }
}

public class DAG {
    public static void main(String[] args) {
        ValueDAGNode v1 = new ValueDAGNode(1);
        ValueDAGNode v2 = new ValueDAGNode(2);
        SumDAGNode sum = new SumDAGNode(List.of(v1, v2));
        System.out.println(sum.value());
    }
}

但是在 C++ 中,当使用此代码时,我得到

exit code 139 (interrupted by signal 11: SIGSEGV)

class AbstractDAGNode {
protected:
    std::vector<std::shared_ptr<AbstractDAGNode>> children{};
    std::vector<std::shared_ptr<AbstractDAGNode>> parents{};

public:
    explicit AbstractDAGNode(std::vector<std::shared_ptr<AbstractDAGNode>> children) {
        this->children = std::move(children);

        for (auto &child: this->children) {
            child->parents.emplace_back(this);
        }
    }

    virtual double Value() = 0;
};

class ValueDAGNode : public AbstractDAGNode {
protected:
    double value{};
public:
    explicit ValueDAGNode(double value) : AbstractDAGNode({}), value(value) {}

    double Value() override {
        return value;
    }
};

class SumDAGNode: public AbstractDAGNode {
public:
    explicit SumDAGNode(const std::vector<std::shared_ptr<AbstractDAGNode>> &children) : AbstractDAGNode(children) {}

    double Value() override {
        double result = 0;
        for (auto &child: children) {
            result += child->Value();
        }
        return result;
    }
};

TEST_CASE("dag1") {
    std::shared_ptr<ValueDAGNode> v1 = std::make_shared<ValueDAGNode>(1);
    std::shared_ptr<ValueDAGNode> v2 = std::make_shared<ValueDAGNode>(2);

    std::vector<std::shared_ptr<AbstractDAGNode>> children = std::vector<std::shared_ptr<AbstractDAGNode>>{v1, v2};

    std::shared_ptr<SumDAGNode> sum_node = std::make_shared<SumDAGNode>(children);
}

问题显然出在这附近

for (auto &child: this->children) {
    child->parents.emplace_back(this);
}

我将当前节点添加到每个子节点的父节点。有一个警告

warning: delete called on 'AbstractDAGNode' that is abstract but has non-virtual destructor
但我不知道这意味着什么或如何解决它。

c++ stdvector smart-pointers
1个回答
0
投票

AbstractDAGNode
需要一个虚拟析构函数。一个空的就可以了:

virtual ~AbstractDAGNode(){}

当你在C++中对类对象指针调用

delete
时,delete需要调用析构函数。 C++ 不(过于简单化警报!)存储对象的真实类型,因此它按照要删除的指针的声明类型并调用
AbstractDAGNode
的析构函数;但是,您的对象不是
AbstractDAGNode
的实例,而是
Value|SumDAGNode
的实例。虚拟析构函数告诉 C++ 调用实际存在的任何对象的析构函数。

这不是默认行为的原因是,在类中拥有虚拟方法并调用它们会在内存和 CPU 上产生很小的开销,而且并不是每个存在的类都需要虚拟析构函数。因此,本着不为不需要的东西付费的精神,您必须明确选择加入。

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