1. 指针和引用使用的场景
1.1. 1. 函数不是构造函数,且参数是只读:用const引用。
如果函数参数是只读的,且不是构造函数,const引用最好。
原因:
如果用指针,用户使用时可能会传nullptr作为参数。
用引用的话,用户基本不可能犯这种错误。
const reference 可以接受匿名变量作为参数。
比如,
1.2. 2.函数不是构造函数,且参数是作为输出(out)参数:用指针。
这种情况用指针最好。因为用户在传指针的时候需要用到取地址操作符(&),这样代码看起来更易懂。
比如:
void copy1(const std::string &a, std::string *b);
void copy2(const std::string &a, std::string &b);
copy1(foo, &bar);//指针输出
copy2(foo, bar);//引用作输出
1.3. 3.函数不是构造函数,且不是只读,但也不是输出参数:用引用。
原因:
- 这种情况用引用比用指针好是引用可以防止不小心传nullptr这种情况。
1.4. 4.函数是构造器,但参数并不参与组成构造类的一部分,那就参考以上几点。
1.5. 5.函数是构造器,且参数参与构造,且参数是可移动的类型:传值。
这种情况直接传值是最好的。比如:
class person {
public:
person(std::string name) : name_{std::move(name)} {}
private:
std::string name_;
};
传值比传const引用好处在于,取决于用户是否需要保留原参,可以省略复制。比如,
Person p{"Jack"}; // 创建一个临时变量,如果一个move;多半情况下编译器会直接创建在Person里
std::string name{"John"};
Person p1{name}; // 这种情况跟用const引用一样,都是一次copy
Person p2{std::move(name)}; // 因为被转化成了右值,name会被直接移动到p2里,省掉一次copy
当然,如果string是像这个例子中这么小的话,move和copy开销是一样的,因为small string optimization。
但如果参数是长的string,或者类似于vector或者map这种heap allocated变量的话,move就比copy开销小非常多。
1.6. 6. 函数是构造器,且参数参与构造,但构造的类只保留参数的引用:传shared_ptr或者自定义指针(不推荐)
首先我非常不提倡这种类型,因为如果这样定义,用户就必须保证name的生命周期比person长。但只看构造器的签名的话又没办法看出来这点。用起来非常麻烦,每次都需要查文档或者读代码才能知道用name构建了person之后能不能销毁name。比如如果你这样定义这个类:
class person {
public:
person(const family_tree &family) : family_{&name} {
family_->add(this);
}
private:
const family_tree *family_;
};
family_tree family{...};
person p{family}; //只看这两行代码你能知道可不可以return p吗?
我一般只有当类是作为函数类型用的时候才会定义这种类型。其他情况,我一般会用shared_ptr作为参数,然后类里也保存shared_ptr作为成员。比如像这样,
class person {
public:
person(std::shared_ptr<family_tree> family) : family_{std::move(family)} {}
private:
std::shared_ptr<family_tree> family_;
};
但这样如果family是stack上的变量且我可以保证family的生命周期比person长怎么办?其实很容易,这样就行了:
int main() {
family_tree family{...};
person p{shared_ptr<family_tree>(&family, [](family_tree *) {})};
...
}