c++智能指针
为了更安全地管理动态内存,c++11引入了智能指针,提供了包括shared_ptr,unique_ptr,weak_ptr三种不同类型的智能指针。
目录结构:
一、三种指针介绍
1.shared_ptr
2.unique_ptr
二、shared指针的基本使用
1.创建shared_ptr对象的两种方法
2.智能指针的使用
3.引用计数
4.复位引用计数函数reset
5.获取C指针函数get
三、shared_ptr指针使用的注意细节
1.不能用栈空间的C指针构造共享指针
2.不能用一个C指针构造一个以上的共享指针
四、智能指针实现多态
一、三种指针介绍
1.shared_ptr
shared_ptr是使用最为广泛的智能指针,推荐shared_ptr为首选智能指针。
2.unique_ptr
std::unique_ptr是C++11标准中用来取代std::auto_ptr的指针容器(在C++11中,auto_ptr被废弃)。它不能与其它unique_ptr类型的指针对象共享所指对象的内存,意味着只能有一个unique_ptr指向特定的对象。这种”所有权”仅能够通过标准库的move函数来转移。unique_ptr是一个删除了拷贝构造函数、保留了移动构造函数的指针封装类型,因此它不能够拷贝和赋值。
unique_ptr<cls> p1(new cls); //unique_ptr初始化
unique_ptr<cls> p2 = p1 //错误,unique_ptr不支持赋值
unique_ptr<cls> p2(p1); //错误,unique_ptr不支持拷贝
p1.reset(); //释放p1指向的内存, p1置空;unique_ptr成员函数reset;
auto ptr = p1.release();//返回普通指针,p1置空,需要执行"delete ptr;"释放内存;
二、shared_ptr指针的基本使用
1.创建shared_ptr对象的两种方法:
a. make_shared
它是最为高效的和安全的创建共享指针的方法,执行后会在堆中分配内存资源并初始化。
b. new
shared_ptr提供了构造函数,以new的方式进行shared_ptr对象的构造。
#include <iostream>
#include <thread>
using namespace std;
class Test
{
public:
Test():age_m(0)
{
cout << "调用Test构造函数" << endl;
}
~Test()
{
cout << "调用Test析构函数" << endl;
}
int getAge()
{
return age_m;
}
void setAge(int age)
{
age_m = age;
}
private:
int age_m;
};
int main()
{
shared_ptr<Test> ptr(new Test());
shared_ptr<Test> makePtr = make_shared<Test>();
cout << ptr->getAge() << endl;
return 0;
}
//打印结果
root@epc:/home/share/eclipse_workspace/exercise/src# ./exercise
调用Test构造函数
调用Test构造函数
0
调用Test析构函数
调用Test析构函数
root@epc:/home/share/eclipse_workspace/exercise/src#
2.shared_ptr指针的使用
shared_ptr指针的使用就是指向原始对象的指针,可以用->访问原始对象的成员,可以用*进行解引用。但它也是一个类对象,它有get成员函数(可以获取原始对象的地址),use_count成语昂函数(返回引用计数)等。
3.引用计数
每个shared_ptr都有一个关联的计数值,通常称为引用计数。无论何时我们拷贝复制一个shared_ptr,计数器都会递增,而且会传递到被拷贝对象。
例如,当用一个shared_ptr初始化另一个shared_ptr,或将它当作参数传递给一个函数以及作为函数的返回值时,它所关联的计数器都会递增。
当我们给shared_ptr赋予一个新值或是shared_ptr被销毁.例如一个局部的shared_ptr离开其作用域时,计数器就会递减。
一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的shared_ptr。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
class Test
{
public:
Test()
{
cout << "调用Test构造函数" << endl;
}
~Test()
{
cout << "调用Test析构函数" << endl;
}
};
int main()
{
shared_ptr<Test> ptr(new Test());
shared_ptr<Test> makePtr = make_shared<Test>();
cout << "初始化后的引用计数ptr=" << ptr.use_count() << endl;
cout << "初始化后的引用计数makePtr=" << makePtr.use_count() << endl;
ptr = makePtr; //赋值后,ptr原对象引用计数减1即为0,执行析构函数;而makePtr的引用计数加1,makePtr的引用计数传递;
cout << "赋值后的引用计数ptr=" << ptr.use_count() << endl;
cout << "赋值后的引用计数makePtr=" << makePtr.use_count() << endl;
return 0;
}
//打印结果
root@epc:/home/share/eclipse_workspace/exercise/src# ./exercise
调用Test构造函数
调用Test构造函数
初始化后的引用计数ptr=1
初始化后的引用计数makePtr=1
调用Test析构函数
赋值后的引用计数ptr=2
赋值后的引用计数makePtr=2
调用Test析构函数
root@epc:/home/share/eclipse_workspace/exercise/src#
三、shared_ptr指针使用的注意细节
1.不能用栈空间的C指针构造共享指针
共享指针在析构的时候会执行delete函数,如果是非new构造,系统会报错;
2.不能用一个C指针构造一个以上的共享指针
用一个C指针构造多个共享指针时,共享指针的引用计数都是单独计算,因此会执行多次delete函数。
//错误的用法
T *a = new T();
shared_ptr<T> ptr1(a);
shared_ptr<T> ptr2(a);
四、智能指针实现多态
题外话:
很多时候c++多态和重载被人混淆,在这里有必要解释一番;
重载:指在一个类中,允许函数名相同但参数类型或者个数不同的成员函数定义;
多态:指派生类和基类中都有相同的成员函数(指函数名和参数都相同);当基类的这个函数为虚函数时,此时派生类的成员函数拥有了覆盖的能力;否则,派生类的成员函数拥有隐藏的能力;
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class Base
{
public:
Base()
{
cout << "基类构造函数" << endl;
}
virtual ~Base()
{
cout << "基类析构函数" << endl;
}
virtual void display()
{
cout << "Base::display()" << endl;
}
};
class Derived : public Base
{
public:
Derived(): Base()
{
cout << "派生类构造函数" << endl;
}
virtual ~Derived()
{
cout << "派生类析构函数" << endl;
}
virtual void display()
{
cout << "Derived::display()" << endl;
}
};
int main()
{
shared_ptr<Base> ptr(new Derived); //shared_ptr
//unique_ptr<Base> ptr(new Derived); //unique_ptr,执行unique_ptr时,请将上一行注释掉
ptr->display();
return 0;
}
//执行shared_ptr的打印结果
root@epc:/home/share/test# ./test
基类构造函数
派生类构造函数
Derived::display()
派生类析构函数
基类析构函数
root@epc:/home/share/test#
//执行unique_ptr的打印结果
root@epc:/home/share/test# ./test
基类构造函数
派生类构造函数
Derived::display()
派生类析构函数
基类析构函数
root@epc:/home/share/test#