class A
{
public:
A(int parameter){};
~A(){};
};
void main()
{
int parameter;
std::vector<std::shared_ptr<A>> aList;
std::shared_ptr<A> aPtr = std::make_shared<A>(A(parameter));
aList.push_back(aPtr);
}
代码大概是上面的情况。
A是一个类。走到`aList.push_back(aPtr)`这个地方的时候,再往下执行,就会触发A的析构函数。
aPtr里有一个指针属性,在这个析构函数里就被析构了。
乍一看是很奇怪的,我把一个共享指针推进一个vector里,共享指针按理说应该计数加一才是正常现象,结果居然析构了!?
这个地方跟push_back还是emplace_back没有关系。
究竟是为什么呢???
我盯着这几行代码盯了半天,总算是看出点端倪。
问题原因:
实际上,无论是aPtr还是aList[0]都没有被析构,监视这两个值可以发现共享指针的计数正常地变成了2。
std::shared_ptr<A> aPtr = std::make_shared<A>(A(parameter));
要害是在这一行!
std::shared_ptr<A> aPtr = std::make_shared<A>(parameter);
这样写才是对的,直接丝滑通过。
为什么会这样呢?下面是我的推测。
我不知道其它编译器是怎么处理的,但是msvc在编译`std::shared_ptr<A> aPtr = std::make_shared<A>(A(parameter)); `这句话时,会建一个A的临时变量,这应该是一个右值,我们姑且不严谨地称之为temp。
紧接着,使用temp来构造aPtr,这一步是值传递。做完之后temp也就没用了,于是在将aPtr推进vector中的时候,temp就被析构了。
这样看,析构了的是temp这个临时的对象,好像也不会有什么问题啊?
然而在我这一次遇到的情况里,A这个类里有一个普通的指针作为属性(注意,我不是说A的共享指针里有A的裸指针,而是说A这个类里有一个指针属性),然后A这个类在值传递的时候,这个指针是浅拷贝,不是深拷贝。
加上这个前提就坏了大事了,temp,aPtr和aList[0]中的指针属性指向的是同一片内存,temp在析构过程中把这个内存空间放掉了,那aPtr和aList[0]中的指针属性直接就指向未定义的内存了。
解决方法,不要像`std::make_shared<A>(A(parameter));`这么写,有隐患。或者可以考虑把A中的指针属性也变成智能指针。