使用智能指针执行 gtest 时遇到 SIGTRAP 错误

Experiencing SIGTRAP error when executing gtest with smart pointers

我有一个 gtest 运行以测试我的 classes 之一中某些属性的分配。当在两个断言都通过的情况下逐步通过调试器时,此测试执行得很好。但是,当代码块完成时,会抛出一个分段错误:

Running main() from C:/Users/user/repos/atvalue_cpp/Google_tests/lib/src/gtest_main.cc
Signal: SIGTRAP (Trace/breakpoint trap)
Signal: ? (Unknown signal)
Process finished with exit code 1

测试如下:

TEST_F(UnitTest, RatedCapacity){
    std::shared_ptr<Unit> parent {new Unit(1, "Parent Unit")};
    std::shared_ptr<Unit> child1 {new Unit(11, "Child 1", parent, 30)};
    std::shared_ptr<Unit> child2 {new Unit(22, "Child 2", parent, 10)};
    std::shared_ptr<Unit> child3 {new Unit(33, "Child 3", parent, 60)};
    std::shared_ptr<Unit> child4 {new Unit(44, "Child 4", parent, 43)};
    std::shared_ptr<Unit> child5 {new Unit(55, "Child 5", child2, 43)};
    std::shared_ptr<Unit> child6 {new Unit(55, "Child 5", child2, 43)};

    EXPECT_EQ(parent->getRatedCapacity(), 0);
    EXPECT_EQ(child2->getRatedCapacity(), 10);
}

和class定义Unitclass:

class Unit {
private:
    unsigned id{};
    std::string name;
    double ratedCapacity;
    std::shared_ptr<Unit> parent;
    std::vector<std::shared_ptr<Unit>> children;
public:
    Unit();
    Unit(unsigned int id, std::string  name);
    Unit(unsigned int id, std::string name, std::shared_ptr<Unit> parent, double capacity);
    ~Unit();

测试中调用的构造函数实现如下:

Unit::Unit(unsigned int id, std::string name, std::shared_ptr<Unit> parent, double_t capacity) :
        id {id},
        name {std::move(name)},
        parent {parent},
        ratedCapacity(capacity) {
    if (parent){
        this->parent = parent;
        auto child = std::shared_ptr<Unit>{this};
        this->parent->addChild(child);
    }
    Unit::counter++;
}

我觉得这个错误是由于其中一个共享指针引用了一块已取消分配的内存。然而,我的印象是(作为 C++ 的相对新手)使用智能指针的全部意义在于首先不必担心这个?显然,我需要更多的学习,所以我非常感谢一些正确方向的智能指示。

行:

auto child = std::shared_ptr<Unit>{this};

将当前正在构建的对象置于新共享指针的所有权之下。

但是,在

等行中构造新对象后
std::shared_ptr<Unit> child1 {new Unit(11, "Child 1", parent, 30)};

Unit 对象再次 置于新共享指针的所有权之下。

这两个共享指针不知道彼此,并且都假定它们拥有 Unit 实例的所有权。如果您复制共享指针,它们当然会跟踪它们共享所有权的事实,但如果您使用相同的原始指针创建新的不相关 std::shared_ptr 对象,则情况并非如此。

因此,共享指针将尝试删除 Unit 个对象两次。


要创建新的 std::shared_ptr 与您无权访问的现有对象共享所有权,您可以使用 std::enable_shared_from_this.

Unit 应该公开继承自 std::enable_shared_from_this 然后你可以使用

auto child = shared_from_this();

但是,由于您在 之前 std::shared_ptr 取得对象的所有权之前在构造函数中执行此操作,因此它不会工作(未定义的行为或因为 C ++17 抛出异常)。

相反,您可以用工厂方法替换构造函数,返回一个 std::shared_ptr,在创建 child.

之前将新对象置于其控制之下

您可以通过仅使用 std::make_shared 而不是显式构造 std::shared_ptr 对象来避免犯此错误。它在某些情况下也有更好的异常处理保证。