- 1:智能指针
- 1.1:创建
- 1.2:智能指针和传统指针的对比
- 1.3:作为参数传递
- 2:智能指针类型
- 2.1:unique_ptr
- 2.2:shared_ptr
- 2.3:weak_ptr
指针是一个变量,用于存储对象的内存地址。指针在C和C ++中广泛用于以下三个主要目的:
- 函数传递
- 在堆上分配对象
- 遍历数组或其他数据结构中的元素
在C风格的编程中,都是使用原始指针。但是,原始指针是许多严重的编程错误的根源。因此,强烈建议不要使用它们,除非它们提供了显著的性能好处。C ++11提供了用于分配对象的智能指针,用于遍历数据结构的迭代器以及用于传递函数的lambda表达式。通过使用智能指针而不是原始指针,可以使程序更安全,更易于调试,更易于理解和维护。
智能指针是在C++11中被引入,std在<memory>头文件的名称空间中定义。主要分为unique_ptr、shared_ptr和weak_ptr。
智能指针
创建
如下是在C++11中定义一个unique_ptr智能指针:
std::unique_ptr<Person> person(new Person);
在C++14中,还可以使用std::make_unique函数来创建:
auto ptr = std::make_unique<Person>();
智能指针和传统指针的对比
void UseRawPointer()
{
// 使用原始指针-不推荐
Person* p = new Person;
// Use p...
p->doSomething();
// Don't forget to delete!
delete p;
}
void UseSmartPointer()
{
// 在堆栈上声明一个原始指针,并将其传递给智能指针
std::unique_ptr<Person> person(new Person);
// Use person...
person->doSomething();
//通过*操作取原始对象
auto obj =*person;
obj.age = 18;
//如果需要在函数执行完前释放资源则可以调用reset方法
//person.reset();
//...
}//person将在此处自动delete
作为参数传递
应该始终在单独的代码行上创建智能指针,而不要在参数列表中创建智能指针,以免由于某些参数列表分配规则而导致细微的资源泄漏:
class Man{
public:
void doSomething(){}
void init(){}
};
void process(Man &man){
man.doSomething();
}
int main() {
std::unique_ptr<Man> ptr(new Man);
ptr->init();
process(*ptr);
return 0;
}
智能指针类型
unique_ptr
如何创建unique_ptr实例并在函数之间传递实例
class Person{
public:
int m_age;
std::string m_name;
Person(std::string& name,int age):m_age(age),m_name(name)
{
}
};
std::unique_ptr<Person> PersonFactory(int&& age,std::string&& name){
return std::make_unique<Person>(name,age);
}
int main() {
//通过std::make_unique创建其Person对象的智能指针
std::string name = "melrose";
auto p1 = std::make_unique<Person>(name,25);
// 将原始指针从一个unique_ptr移到另一个
auto p2 = std::move(p1);
//如果再通过p1去获取原始指针会报错
printf("p2: %s %d\n",p2->m_name.c_str(),p2->m_age);
//隐式移动操作到存储结果的变量中
auto p3 = PersonFactory(18,"Jhon");
return 0;
}
在数组和vector中使用unique_ptr
//在vector中使用
void PersonVector(){
std::vector<std::unique_ptr<Person>> persons;
persons.push_back(std::make_unique<Person>("t1",22));
persons.push_back(std::make_unique<Person>("t2",23));
persons.push_back(std::make_unique<Person>("t3",24));
for (const auto &p:persons) {
printf("%s %d\n",p->m_name.c_str(),p->m_age);
}
}
//数组中使用unique_ptr
void Array(){
// 创建一个由5个整数组成的数组的unique_ptr。
auto p = std::make_unique<int[]>(5);
// 初始化数组。
for (int i = 0; i < 5; ++i)
{
p[i] = i;
}
}
shared_ptr
shared_ptr类型也是C ++11标准库中出来的智能指针,该指针是为可能需要多个所有者来管理内存中对象生命周期的方案所设计的。初始化该智能指针之后,shared_ptr可以被复制,在函数参数中按值传递它,并可将其分配给其他shared_ptr实例。所有实例都指向同一个对象,并共享对一个控制块的访问权,每当shared_ptr添加新的,超出范围的或重置时,该控制块都会递增和递减引用计数。当引用计数达到0时,控制块将删除内存资源及其本身。
下图显示了shared_ptr指向一个内存位置的多个实例:
应该在第一次创建内存资源时就使用make_shared函数来创建shared_ptr。它使用相同的控制块和资源分配内存,从而减少了构造开销。如果不使用make_shared,则必须使用显式new表达式创建对象,然后再将其传递给shared_ptr构造函数。以下示例显示了shared_ptr与新对象一起声明和初始化的各种方法:
//使用std::make_shared函数来创建shared_ptr
auto sp1 = std::make_shared<Person>();
// 通过构造函数创建
std::shared_ptr<Person> sp2(new Person);
//等效于:std::shared_ptr<Person> sp5;
std::shared_ptr<Person> sp5(nullptr);
sp5 = std::make_shared<Person>();
下面的示例演示如何声明和初始化shared_ptr实例,这些实例具有已由另一个对象分配的对象的共享所有权shared_ptr创建:
auto sp1 = std::make_shared<Person>();
sp1->age = 25;
//用复制构造函数初始化, 引用计数增加
auto sp2(sp1);
//通过分配进行初始化, 引用计数增加
auto sp3 = sp1;
//用另一个shared_ptr初始化,sp1和sp2交换指针以及引用计数
sp1.swap(sp2);
//使用use_count方法可以查看引用的数量
printf("use count:%d",sp1.use_count());
可以通过以下方式将shared_ptr传递给另一个函数:
- 传递shared_ptr按值传递。这将调用复制构造函数,增加引用计数,并使被调用方成为所有者。此操作的开销很少,具体取决于shared_ptr要传递的对象数。当调用方和被调用方之间的隐式或显式代码约定要求被调用方为所有者时,可以使用此选项。
- 传递shared_ptr引用或const引用。在这种情况下,引用计数不会增加,只要被调用方没有超出范围,被调用就可以访问该指针。或者,被调用方可以决定shared_ptr基于引用创建一个,并成为共享所有者。当调用者不了解被调用者时,或者必须传递shared_ptr并且出于性能原因而希望避免复制操作时,可以使用此选项。
- 将基础指针或引用传递给基础对象。这使被调用者可以使用该对象,但不能使其共享所有权或延长生存期。如果被调用方通过原始指针创建了shared_ptr,则新对象shared_ptr将独立于原始对象,并且不会控制原始资源。当调用者和被调用者之间的明确指定调用者保留shared_ptr生命周期的所有权时,可以使用此选项。
- 在决定如何传递时shared_ptr,需确定被调用方是否必须共享基础资源的所有权。所有者是一个对象或函数,可以根据需要保持其生存时间。如果调用者必须保证被调用者可以将指针的寿命延长到其(函数的)寿命之外,请使用第一个选项。如果不在乎被调用方是否延长生存期,请通过引用传递并让被调用方复制或不复制。
- 如果必须授予辅助函数访问基础指针的权限,并且知道该辅助函数将仅使用该指针并在调用函数返回之前返回,则该函数不必共享基础指针的所有权。它只需要在调用者的生存期内访问指针shared_ptr。在这种情况下,可以按引用安全地传递shared_ptr,或将原始指针或引用传递给基础对象。
weak_ptr
std::vector<std::weak_ptr<Person>> persons;
auto p = std::make_shared<Person>();
persons.push_back(std::weak_ptr<Person>(p));