1 概述
C++11引入了智能指针std::shared_ptr,不过std::shared_ptr随然好用,但使用不当就可能导致互相引用,导致内存无法释放。本文以实例来演示相互引用,以及解决办法。
2 实例
本实例定义两个类型Person和Car,Person成员中有Car共享指针变量car_,Car成员中有Person共享指针变量dirver_。
2.1 代码
#include <iostream>
#include <memory>
#include <string>
class Car;
typedef std::shared_ptr<Car> CarPtr;
class Person
{
std::string name_;
CarPtr car_;
public:
Person(const char* name)
: name_(name)
{
std::cout << "Person()" << std::endl;
}
~Person()
{
std::cout << "~Person()" << std::endl;
}
std::string name() const { return name_; }
void setCar(CarPtr const& car);
void say();
};
typedef std::shared_ptr<Person> PersonPtr;
class Car
{
PersonPtr dirver_;
std::string name_;
public:
Car(const char* name)
: name_(name)
{
std::cout << "Car()" << std::endl;
}
~Car()
{
std::cout << "~Car()" << std::endl;
}
std::string name() const { return name_; }
void setDriver(PersonPtr const& persion)
{
dirver_ = persion;
}
void run()
{
if(dirver_)
std::cout << dirver_->name() << " is driving the " << name() << "." << std::endl;
else
std::cout << "now has not a driver!" << std::endl;
}
};
void Person::setCar(CarPtr const& car) { car_ = car; }
void Person::say()
{
if(car_)
std::cout << "I have a " << car_->name() << "!" << std::endl;
else
std::cout << "I have not any car!";
}
int main(int argc, char *argv[])
{
auto car = std::make_shared<Car>("Jeep");
auto dirver = std::make_shared<Person>("James");
car->setDriver(dirver);
dirver->setCar(car);
dirver->say();
car->run();
return 0;
}
运行结果:
Car()
Person()
I have a Jeep!
James is driving the Jeep.
从运行结果看Car和Persion的析构函数都没有调用,导致内存泄露。
2.2 原因分析,
修改代码如下:
int main(int argc, char *argv[])
{
auto car = std::make_shared<Car>("Jeep");
auto dirver = std::make_shared<Person>("James");
car->setDriver(dirver);
dirver->setCar(car);
dirver->say();
car->run();
std::cout << "car's use count: " << car.use_count() << std::endl;
std::cout << "person's use count: " << dirver.use_count() << std::endl;
return 0;
}
运行结果
Car()
Person()
I have a Jeep!
James is driving the Jeep.
car's use count: 2
person's use count: 2
从运行结果看:
- 对象car和dirver的引用计数都是2.
- 程序运行结束后car和dirver的析构函数后car和dirver引用计数减1变为1。
- 最终由于引用计数是1而不是0导致,car和driver管理的指针没有释放,相应Car和Person析构函数也就没有调用。
3 解决
对于该问题,C++11引入std::weak_ptr来解决该问题。std::weak_ptr管理std::shared_ptr,不会更加共享指针的引用计数,使用时通过lock接口返回共享指针来使用。
Car代码修改如下:
class Car
{
std::weak_ptr<Person> dirver_;
std::string name_;
public:
Car(const char* name)
: name_(name)
{
std::cout << "Car()" << std::endl;
}
~Car()
{
std::cout << "~Car()" << std::endl;
}
std::string name() const { return name_; }
void setDriver(PersonPtr const& persion)
{
dirver_ = persion;
}
void run()
{
PersonPtr dirver = dirver_.lock();
if(dirver)
std::cout << dirver->name() << " is driving the " << name() << "." << std::endl;
else
std::cout << "now has not a driver!" << std::endl;
}
};
修改说明:
- 成员变量dirver_类型定义为std::weak_ptr
- run函数调用dirver_的lock函数返回共享指针使用。
运行结果:
Car()
Person()
I have a Jeep!
James is driving the Jeep.
~Person()
~Car()
从运行结果看:
- Person和Car的析构函数都调用了
- 问题解决。