std::make_shared 的奇怪行为
Strange behaviour of std::make_shared
我有一个我无法理解的非常奇怪的行为。
此测试通过:
CipString str = *std::make_shared<CipString>("Bye!").get();
EXPECT_EQ(static_cast<std::string>(str), "Bye!");
但这不是:
CipString *str = std::make_shared<CipString>("Bye!").get();
EXPECT_EQ(static_cast<std::string>(*str), "Bye!");
我收到一个错误:
Expected: static_cast(*str)
Which is: "p\x15\x97\x1"
To be equal to: "Bye!"
CipString 的代码:
class CipString{
public:
CipString(const std::string& str) {
length = str.size();
string.reset(new uint8_t[length]);
std::copy(str.begin(), str.end(), string.get());
}
operator std::string() const {
std::string str("", length);
std::copy(string.get(), string.get() + length, str.begin());
return str;
}
uint16_t length; /**< Length of the String (16 bit value) */
std::shared_ptr<uint8_t> string; /**< Pointer to the string data */
};
这一行:
CipString *str = std::make_shared<CipString>("Bye!").get();
创建一个 shared_ptr,它在 ;
之后被销毁。
str
之后是一个悬挂指针,您的测试通过访问释放的内存调用未定义的行为。
你基本上是在断言垃圾内存。
CipString str = *std::make_shared<CipString>("Bye!").get();
EXPECT_EQ(static_cast<std::string>(str), "Bye!");
分解为:
1) 调用 make_shared 到 return 一个 shared_ptr
2) 获取底层对象的地址
3) 使用指向底层对象的指针构造 CipString
4) 摧毁shared_ptr
即'fine'
这个这个分解为:
CipString *str = std::make_shared<CipString>("Bye!").get();
1) 构造一个shared_ptr
2) 获取底层对象的地址
3) 从中创建一个临时的 CipString 并将地址存储在 str
4) 摧毁shared_ptr
5) 销毁临时 CipString(str 指向的)
这意味着:
EXPECT_EQ(static_cast<std::string>(*str), "Bye!");
成为未定义的行为
你可以改写这个:
CipString str = *std::make_shared<CipString>("Bye!").get();
EXPECT_EQ(static_cast<std::string>(str), "Bye!");
如:
CipString str;
{
auto sp = std::make_shared<CipString>("Bye!");
str = *sp.get(); // here a copy is made (in original code copy initialization)
// here sp will get destroyed
}
// here str contains correct "Bye!" value
并且这没有未定义的行为。另一方面,这段代码:
CipString *str = std::make_shared<CipString>("Bye!").get();
将重写为:
CipString *str;
{
auto sp = std::make_shared<CipString>("Bye!");
str = sp.get(); // here a pointer is assigned to str, no copy is done as above
// here sp will get destroyed, and also pointer to
// which str points will be a dangling pointer
}
// here str should not be used - as it is UB
我有一个我无法理解的非常奇怪的行为。
此测试通过:
CipString str = *std::make_shared<CipString>("Bye!").get();
EXPECT_EQ(static_cast<std::string>(str), "Bye!");
但这不是:
CipString *str = std::make_shared<CipString>("Bye!").get();
EXPECT_EQ(static_cast<std::string>(*str), "Bye!");
我收到一个错误:
Expected: static_cast(*str)
Which is: "p\x15\x97\x1"
To be equal to: "Bye!"
CipString 的代码:
class CipString{
public:
CipString(const std::string& str) {
length = str.size();
string.reset(new uint8_t[length]);
std::copy(str.begin(), str.end(), string.get());
}
operator std::string() const {
std::string str("", length);
std::copy(string.get(), string.get() + length, str.begin());
return str;
}
uint16_t length; /**< Length of the String (16 bit value) */
std::shared_ptr<uint8_t> string; /**< Pointer to the string data */
};
这一行:
CipString *str = std::make_shared<CipString>("Bye!").get();
创建一个 shared_ptr,它在 ;
之后被销毁。
str
之后是一个悬挂指针,您的测试通过访问释放的内存调用未定义的行为。
你基本上是在断言垃圾内存。
CipString str = *std::make_shared<CipString>("Bye!").get();
EXPECT_EQ(static_cast<std::string>(str), "Bye!");
分解为:
1) 调用 make_shared 到 return 一个 shared_ptr
2) 获取底层对象的地址
3) 使用指向底层对象的指针构造 CipString
4) 摧毁shared_ptr
即'fine'
这个这个分解为:
CipString *str = std::make_shared<CipString>("Bye!").get();
1) 构造一个shared_ptr
2) 获取底层对象的地址
3) 从中创建一个临时的 CipString 并将地址存储在 str
4) 摧毁shared_ptr
5) 销毁临时 CipString(str 指向的)
这意味着:
EXPECT_EQ(static_cast<std::string>(*str), "Bye!");
成为未定义的行为
你可以改写这个:
CipString str = *std::make_shared<CipString>("Bye!").get();
EXPECT_EQ(static_cast<std::string>(str), "Bye!");
如:
CipString str;
{
auto sp = std::make_shared<CipString>("Bye!");
str = *sp.get(); // here a copy is made (in original code copy initialization)
// here sp will get destroyed
}
// here str contains correct "Bye!" value
并且这没有未定义的行为。另一方面,这段代码:
CipString *str = std::make_shared<CipString>("Bye!").get();
将重写为:
CipString *str;
{
auto sp = std::make_shared<CipString>("Bye!");
str = sp.get(); // here a pointer is assigned to str, no copy is done as above
// here sp will get destroyed, and also pointer to
// which str points will be a dangling pointer
}
// here str should not be used - as it is UB