C++参数类型的合理选用
前言
- 在传统C++中, 函数的参数类型主要有三种:
传值 、传指针 和传引用 - C++11开始,多了一种叫做
右值引用 的类型,以及一些智能指针类型(shared_ptr 、weak_ptr 、unique_ptr )
各参数类型的最佳适用场景
传值
- 传值开销 比 传指针和引用开销更小,如:
char 比char * 和char & 占用内存更小 - 函数内部明确需要参数的副本
传指针
- 结构体,体积较大,并且设计到不通类型的地址转换
- 内存字节操作
- 参数需要分为空与非空
- 多个对象内存连续
传引用
- 单个对象,体积较大
- 需要对参数对象做修改
- 函数内明确不能使用空对象
传右值引用
传智能指针
- 需要区分空与非空,并且设计对象生命周期管理的,如:
当参数对象需要共享时,使用 shared_ptr 当参数对象需要独占,只能进行转移时,使用 unique_ptr 参数中不使用 weak_ptr , weak_ptr 仅用于共享对象之间的互相引用解决引用环
const约束
- 当参数明确表示函数内部不会进行修改时,无论是引用类型参数还是指针类型参数,都应该加上
const 约束,如:
void funcRef(const Arg& arg)
{
}
void funcPointer(const Arg *arg)
{
if (arg == nullptr) {
} else {
}
}
注意:
严格执行必要的参数const约束 后会发现,当调用了某些类的成员函数并且这些函数同样不会对自身有任何改动,但却编译不通过。这是为什么呢?
成员函数的const约束
上述问题中的答案其实很简单,因为调用的成员函数虽然没有对成员有改动,但是成员函数并没有标记为const, 没有标记为const的成员函数,只有在对象本身处于非常量状态下才能调用。因此,如果明确表示成员函数不会对成员变量有改动,需要显示的在函数尾部标记为const 。如:
class Person {
explicit Person(const std::string& name):name_(name) {}
~Person() = default;
std::string name() const
{
return nane_;
}
int age() const
{
return age_;
}
private:
std::string name_;
int age_ {0};
};
void callPerson(const Person& person)
{
std::cout << person.name() << ", your turn" << std::endl;
}
常左值引用与右值引用的合并
当某个函数逻辑内部会对参数做副本时,传统做法是使用常引用类型,然后在内部使用拷贝函数显式定义一个副本对象,C++11后,除了要求要写常左值引用版本的参数类型外,还需要实现右值引用版本的参数,保证传入的对象如果是一个右值,则会自动进行移动操作,而不是拷贝
针对上述情况,我们可以将两种情况进行合并,实现一个传值类型的参数版本即可,这样,既可以传左值引用自动拷贝(传值自动拷贝),也可以传右值引用将外部对象直接移动到函数参数内。
将一个右值引用移动到参数上,可以大大减少对象的拷贝开销,增加对象的利用率
|