c++难点之一就是要比较关注内存管理的问题,如果内存不及时释放就会造成内存泄漏。智能指针出现之前,程序员都是手动释放已经申请的堆内存,但有时候比较容易忘记,也有可能就是程序执行异常了,导致释放内存的代码没有执行,例如以下代码就需要自己手动释放内存。
class Test1206
{
public:
Test1206(const char *str)
{
int len = strlen(str);
p = new char[len+1];
strncpy(p, str, len);
p[len] = '\0';
cout << "test1206 constructed" << endl;
}
~Test1206()
{
delete p;//手动释放
cout << "test1206 deconstructed" << endl;
}
void print()
{
cout << p << endl;
}
private:
char *p = NULL;
};
int main()
{
Test1206 *t = new Test1206("Hello,World!");
t->print();
delete t;//手动释放
return 0;
}
总之手动释放内存很容易导致内存泄漏。智能指针就是用来解决手动释放内存的问题的,比较常用的智能指针就是share_ptr。
(1) shared_ptr(存在线程安全问题)
struct test_shared_ptr_t
{
~test_shared_ptr_t()
{
cout << "destructed" << endl;
}
int a = 0;
int b = 0;
int c = 0;
};
int main()
{
shared_ptr<int> ptr = make_shared<int>(2);//自动释放内存 如果不填2 会有一个默认的值
cout << ptr.get() << " " << *(ptr.get()) << " " <<*ptr << endl;
shared_ptr<test_shared_ptr_t> ptr2(new test_shared_ptr_t);//自动释放内存
cout << ptr2.get() << " " << ptr2->a << " " << ptr2->b << " " << ptr2->c << endl;
return 0;
}
程序输出为:
0000022A86CE9F80 2 2
0000022A86CEFE60 0 0 0
destructed
从上面的程序可以发现,shared_ptr指针有两种初始化的方式,一种是通过make_shared(推荐使用这次方式),另外一种是通过把new出来的指针传给shared_ptr的构造函数。比较有意思的是智能指针ptr既可以使用.运算符也可以使用->运算符。
class TestSharePtr
{
public:
void insert_data()
{
auto tmp = make_shared<test_shared_ptr_t>();
tmp->a = 10;
tmp->b = 10;
tmp->c = 10;
share_map.insert(std::make_pair(10, tmp));
}
void delete_data()
{
if (share_map.count(10)) {
share_map.erase(10);
std::cout << "erase data" << std::endl;
}
}
~TestSharePtr()
{
std::cout << "test_share_ptr deleted!" << std::endl;
}
std::map<int, std::shared_ptr< test_shared_ptr_t>> share_map;
};
int main()
{
TestSharePtr tst;
tst.insert_data();
while (1) {
Sleep(1000);
tst.delete_data();
}
return 0;
}
从上面的例子可以发现erase智能指针对象的时候会自动调用构造函数。
struct test_shared_ptr_t
{
~test_shared_ptr_t()
{
cout << "test_shared_ptr_t destructed" << endl;
}
int a = 0;
int b = 0;
int c = 0;
};
class TestSharePtr
{
public:
TestSharePtr()
{
test1 = std::make_shared<test_shared_ptr_t>();
test2 = new test_shared_ptr_t;
}
~TestSharePtr()
{
std::cout << "TestSharePtr deleted!" << std::endl;
delete test2;
}
std::shared_ptr<test_shared_ptr_t> test1;
test_shared_ptr_t* test2;
};
int main()
{
TestSharePtr tst;
return 0;
}
从上例可以看出来,tst析构的时候,会自动调用其类成员test1的构造函数,释放对应的内存空间,但是test2却不会,需要手动delete。
(2)auto_ptr
在c++11中已废弃,被unique_ptr所代替,原因是转移指针所有权时(不会报错),再去引用以前的auto_ptr会导致程序崩溃
(3)unique_ptr
没有引用计数器,保证一个对象只被一个unique_ptr指向它(多个会报错),不能使用赋值转移(会报错),但是可以使用move移动转移或者移动构造。
(4)weak_ptr
它一般是配合shared_ptr使用,weak_ptr不会增加引用计数,用来解决shared_ptr循环引用时,内存泄漏的问题。
class CB;
class CA;
class CA
{
public:
CA() {}
~CA() { std::cout << "A destructed" << std::endl; }
void Register(const std::shared_ptr<CB>& sp)
{
m_sp = sp;
}
private:
std::shared_ptr<CB> m_sp; //改为weak_ptr则不会有循环引用的问题
};
class CB
{
public:
CB() {};
~CB() { std::cout << "B destructed" << std::endl; };
void Register(const std::shared_ptr<CA>& sp)
{
m_sp = sp;
}
private:
std::shared_ptr<CA> m_sp;
};
int main()
{
std::shared_ptr<CA> spa(new CA);
std::shared_ptr<CB> spb(new CB);
spb->Register(spa);
spa->Register(spb);
printf("%d,%d\n", spb.use_count()); // 2
printf("%d,%d\n", spa.use_count()); // 2
return 0;
}
参考资料:
http://c.biancheng.net/view/7898.html 使用
https://www.cnblogs.com/zeppelin5/p/10083597.html
https://blog.csdn.net/shaosunrise/article/details/85228823
https://blog.csdn.net/kof0101/article/details/51884634
https://www.jianshu.com/p/c6b7b4e15f9d
https://blog.csdn.net/mayh554024289/article/details/96874719
https://blog.csdn.net/qq_22642239/article/details/102667553
https://blog.csdn.net/fengbingchun/article/details/52202007
https://blog.csdn.net/worldwindjp/article/details/18843087 实现
https://www.cnblogs.com/wxquare/p/4759020.html
https://www.cnblogs.com/diysoul/p/5930361.html
https://blog.csdn.net/yagerfgcs/article/details/72886630
https://blog.csdn.net/CPriLuke/article/details/79462791