C++ shared_ptr 作为参数和返回值的比较
shared_ptr做参数
值传递和引用传递
智能指针的规则和普通的参数相同,我们可以把它当作是一个普通的类型,不要去想指针的那套规则了。
和普通类型一样,智能指针的值传递比引用传递多了一次内存的拷贝,但是和普通类型的值传递稍微不同的是,值传递时修改实参不会产生问题(这里类比指针)。
下面来看个例子:
void func1(PersonShardPtr p1)
{
std::cout << "func1, " << "memory adress:" << &p1 << ", value:" << p1 << ", use_count:" << p1.use_count() << endl;
p1->a= 100;
}
void func2(PersonShardPtr& p2)
{
std::cout << "func2, " << "memory adress:" << &p2 << ", value:" << p2 << ", use_count:" << p2.use_count() << endl;
p2->b= 100;
}
int main(int argc, char *argv[])
{
std::shared_ptr<Person> p = std::make_shared<Person>();
std::cout << "main1, " << "memory adress:" << &p << ", value:"<< p<< ", use_count:" << p.use_count() << endl;
func1(p);
std::cout << "main2, " << "memory adress:" << &p << ", value:" << p << ", use_count:" << p.use_count() << endl;
func2(p);
std::cout << "main3, " << "memory adress:" << &p << ", value:" << p << ", use_count:" << p.use_count() << endl;
std::cout << "main4, " << "a value is:" << p->a << ", b value is:" << p->b << endl;
}
从代码和运行结果,我们可以看到:
1.调用func1时,p1的内存地址发生了变化,所以引用计数加了1,p2的内存地址不变,所以引用计数不变;
2.p中a和b的值都被成功的修改了。
对于通常的使用来说,值传递和引用传递都能满足我们的要求, 但是在下面的场景值传递会出现问题,我们在const里详细了解一下。
const的作用
首先我们先看一个场景:
void func1(PersonShardPtr p)
{
std::cout << "func1, " << "memory adress:" << &p << ", value:" << p << ", use_count:" << p.use_count() << endl;
p = std::make_shared<Person>();
p->a= 100;
//return nullptr;
}
void func2(PersonShardPtr& p)
{
std::cout << "func2, " << "memory adress:" << &p << ", value:" << p << ", use_count:" << p.use_count() << endl;
p = std::make_shared<Person>();
p->b= 100;
//return nullptr;
}
void func3(const PersonShardPtr p)
{
std::cout << "func4, " << "memory adress:" << &p << ", value:" << p << ", use_count:" << p.use_count() << endl;
//p = std::make_shared<Person>(); //打开这个注释编译会出错,因为声明为const。
p->c= 100;
}
int main(int argc, char *argv[])
{
std::shared_ptr<Person> p = std::make_shared<Person>();
std::cout << "main1, " << "memory adress:" << &p << ", value:"<< p<< ", use_count:" << p.use_count() << endl;
func1(p);
std::cout << "main2, " << "memory adress:" << &p << ", value:" << p << ", use_count:" << p.use_count()<<endl;
func2(p);
std::cout << "main3, " << "memory adress:" << &p << ", value:" << p << ", use_count:" << p.use_count() << endl;
std::cout << "main4, " << "a value is:" << p->a << ", b value is:" << p->b << endl;
func3(p);
}
从运行结果我们可以看到func1中p重新分配了内存,所以a的值为1,func2中重新分配内存后,我们发现p的值发生了变化。这个现象和传递指针是一样的。所以const的作用可以防止智能指针指向新的对象,但是注意const不能阻止修改p指向对象的值。
shared_ptr作为返回值
shared_ptr作为返回值没有特别多需要强调了,由于RVO的原因,通常情况下shared_ptr返回值类型就够了,而且返回引用类型需要保证这个shared_ptr不能是临时变量,所以除了特殊使用场景,是没有必要的。
下面看一个例子:
PersonShardPtr func4()
{
PersonShardPtr p = std::make_shared<Person>();
std::cout << "func4, " << "memory adress:" << &p << ", value:" << p << ", use_count:" << p.use_count() << endl;
return p;
}
int main(int argc, char *argv[])
{
PersonShardPtr p = func4();
std::cout << "main, " << "memory adress:" << &p << ", value:" << p << ", use_count:" << p.use_count() << endl;
}
可以看到,由于RVO的原因,他们的地址是相同的。
这里提到一点,由于编译器的原因,当我们需要返回空指针的时候,null/nullptr 有可能不会被编译器识别导致编译错误,所以返回空指针建议使用PersonShardPtr();