我正在尝试用 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
但我不知道这意味着什么或如何解决它。
AbstractDAGNode
需要一个虚拟析构函数。一个空的就可以了:
virtual ~AbstractDAGNode(){}
当你在C++中对类对象指针调用
delete
时,delete需要调用析构函数。 C++ 不(过于简单化警报!)存储对象的真实类型,因此它按照要删除的指针的声明类型并调用 AbstractDAGNode
的析构函数;但是,您的对象不是 AbstractDAGNode
的实例,而是 Value|SumDAGNode
的实例。虚拟析构函数告诉 C++ 调用实际存在的任何对象的析构函数。
这不是默认行为的原因是,在类中拥有虚拟方法并调用它们会在内存和 CPU 上产生很小的开销,而且并不是每个存在的类都需要虚拟析构函数。因此,本着不为不需要的东西付费的精神,您必须明确选择加入。