make_shared 偶尔会创建重复的指针吗?
Can make_shared occasionally create duplicate pointers?
我的一些单元测试在每次测试中大约有 25% 的时间未通过。问题是当我创建一个 "Entity" 对象时,我使用 std::make_shared 函数创建了一个指向另一个对象的指针。似乎偶尔,单独的 Entity 对象中的指针指向同一个转换对象。我不确定这怎么可能。相关代码如下:
实体构造函数:
Entity::Entity(std::shared_ptr<Shape> i_shape) :
shape {i_shape}
{
transform = std::make_shared<Transform> (Vector2f(0.0f, 0.0f), Vector2f(1.0f, 1.0f), 0.0f);
}
Visual Studio 2017 单元测试代码揭示了问题:
TEST_METHOD(CircleCollisionTest2)
{
Entity* testCircleEnt1;
Entity* testCircleEnt2;
std::shared_ptr<Circle> testCircle1 = std::make_shared<Circle>(60.0f);
std::shared_ptr<Circle> testCircle2 = std::make_shared<Circle>(60.0f);
testCircleEnt1 = &Entity(testCircle1);
testCircleEnt2 = &Entity(testCircle2);
testCircleEnt1->GetTransform()->SetPostion(Vector2f(0, 0));
testCircleEnt2->GetTransform()->SetPostion(Vector2f(200, 0));
Assert::IsFalse(Physics::TestCollision(testCircleEnt1, testCircleEnt2));
}
当我实例化我的 Entity 对象时,make_shared 正在创建指向相同 Transform 对象的指针。我已经确认它们在调试模式下拥有相同的地址,因此它不应该涉及我的 getter 或 setter 或类似的东西。
此外,每当发生这种情况时,失败的测试需要大约 150 毫秒才能完成,而不是大约 5 毫秒。怎么会发生大约 25% 的时间而不是 100% 或 0%?我认为它可能与 Visual studio 测试框架有关,但我在定制测试中复制了这些结果。
非常欢迎任何帮助。谢谢!
没关系,只是你通过获取临时对象的地址创建了两个悬挂指针,然后甚至取消引用它们,调用你观察到的未定义行为。
您的代码代码有未定义的行为。当你这样做时
testCircleEnt1 = &Entity(testCircle1);
testCircleEnt2 = &Entity(testCircle2);
您创建了两个临时对象并存储了它们的地址。首先,那不应该编译。如果是,您需要打开 wanring/error 选项。其次,临时对象在它们所在的完整表达式结束时被销毁,这样就留下了两个悬空指针。你的代码应该是
TEST_METHOD(CircleCollisionTest2)
{
std::shared_ptr<Circle> testCircle1 = std::make_shared<Circle>(60.0f);
std::shared_ptr<Circle> testCircle2 = std::make_shared<Circle>(60.0f);
testCircle1->GetTransform()->SetPostion(Vector2f(0, 0));
testCircle2->GetTransform()->SetPostion(Vector2f(200, 0));
Assert::IsFalse(Physics::TestCollision(testCircle1.get(), testCircle2.get()));
}
由于您正在使用 visual studio,您应该启用 /permissive-
以加强对 C++ 标准的遵守。
我很惊讶这段代码可以编译,因为它不是合法的 C++:
testCircleEnt1 = &Entity(testCircle1);
testCircleEnt2 = &Entity(testCircle2);
这里,表达式Entity(testCircle1)
产生一个临时的Entity
对象,在C++中不能取临时对象的地址。如果您这样做,您会 运行 陷入麻烦,因为 C++ 中的临时对象在创建它们的语句完成 运行ning 后不复存在,留下指向不再存在的对象的指针。这会导致未定义的行为,因此会导致周期性崩溃。
如果你想创建新对象让 testCircleEnt1
和 testCircleEnt2
指向,你可以使用 new
:
testCircleEnt1 = new Entity(testCircle1);
testCircleEnt2 = new Entity(testCircle2);
但是,在这一点上,您可能会过得更好
- 只是将
testCircleEnt1
和 testCircleEnt2
声明为实际的 Entity
对象而不是指向它们的指针,或者
- 将
testCircleEnt1
和 testCircleEnt1
声明为 std::shared_ptr<Entity>
而不是原始指针。
我的一些单元测试在每次测试中大约有 25% 的时间未通过。问题是当我创建一个 "Entity" 对象时,我使用 std::make_shared 函数创建了一个指向另一个对象的指针。似乎偶尔,单独的 Entity 对象中的指针指向同一个转换对象。我不确定这怎么可能。相关代码如下:
实体构造函数:
Entity::Entity(std::shared_ptr<Shape> i_shape) :
shape {i_shape}
{
transform = std::make_shared<Transform> (Vector2f(0.0f, 0.0f), Vector2f(1.0f, 1.0f), 0.0f);
}
Visual Studio 2017 单元测试代码揭示了问题:
TEST_METHOD(CircleCollisionTest2)
{
Entity* testCircleEnt1;
Entity* testCircleEnt2;
std::shared_ptr<Circle> testCircle1 = std::make_shared<Circle>(60.0f);
std::shared_ptr<Circle> testCircle2 = std::make_shared<Circle>(60.0f);
testCircleEnt1 = &Entity(testCircle1);
testCircleEnt2 = &Entity(testCircle2);
testCircleEnt1->GetTransform()->SetPostion(Vector2f(0, 0));
testCircleEnt2->GetTransform()->SetPostion(Vector2f(200, 0));
Assert::IsFalse(Physics::TestCollision(testCircleEnt1, testCircleEnt2));
}
当我实例化我的 Entity 对象时,make_shared 正在创建指向相同 Transform 对象的指针。我已经确认它们在调试模式下拥有相同的地址,因此它不应该涉及我的 getter 或 setter 或类似的东西。
此外,每当发生这种情况时,失败的测试需要大约 150 毫秒才能完成,而不是大约 5 毫秒。怎么会发生大约 25% 的时间而不是 100% 或 0%?我认为它可能与 Visual studio 测试框架有关,但我在定制测试中复制了这些结果。
非常欢迎任何帮助。谢谢!
没关系,只是你通过获取临时对象的地址创建了两个悬挂指针,然后甚至取消引用它们,调用你观察到的未定义行为。
您的代码代码有未定义的行为。当你这样做时
testCircleEnt1 = &Entity(testCircle1);
testCircleEnt2 = &Entity(testCircle2);
您创建了两个临时对象并存储了它们的地址。首先,那不应该编译。如果是,您需要打开 wanring/error 选项。其次,临时对象在它们所在的完整表达式结束时被销毁,这样就留下了两个悬空指针。你的代码应该是
TEST_METHOD(CircleCollisionTest2)
{
std::shared_ptr<Circle> testCircle1 = std::make_shared<Circle>(60.0f);
std::shared_ptr<Circle> testCircle2 = std::make_shared<Circle>(60.0f);
testCircle1->GetTransform()->SetPostion(Vector2f(0, 0));
testCircle2->GetTransform()->SetPostion(Vector2f(200, 0));
Assert::IsFalse(Physics::TestCollision(testCircle1.get(), testCircle2.get()));
}
由于您正在使用 visual studio,您应该启用 /permissive-
以加强对 C++ 标准的遵守。
我很惊讶这段代码可以编译,因为它不是合法的 C++:
testCircleEnt1 = &Entity(testCircle1);
testCircleEnt2 = &Entity(testCircle2);
这里,表达式Entity(testCircle1)
产生一个临时的Entity
对象,在C++中不能取临时对象的地址。如果您这样做,您会 运行 陷入麻烦,因为 C++ 中的临时对象在创建它们的语句完成 运行ning 后不复存在,留下指向不再存在的对象的指针。这会导致未定义的行为,因此会导致周期性崩溃。
如果你想创建新对象让 testCircleEnt1
和 testCircleEnt2
指向,你可以使用 new
:
testCircleEnt1 = new Entity(testCircle1);
testCircleEnt2 = new Entity(testCircle2);
但是,在这一点上,您可能会过得更好
- 只是将
testCircleEnt1
和testCircleEnt2
声明为实际的Entity
对象而不是指向它们的指针,或者 - 将
testCircleEnt1
和testCircleEnt1
声明为std::shared_ptr<Entity>
而不是原始指针。