尽量以pass-by-reference-to-const 替换 pass-by-value。前者比较高效,并可避免切割问题。
【内置类型、STL的迭代器、函数对象】比较适合pass-by-value。
--------------------------
class Person{
public:
Person();
virtual ~Person();
...
private:
std::string name;
std::string address;
};
class Student:public Person{
public:
Student();
~Student();
...
private:
std::string schoolName;
std::string schoolAddress;
};
定义成函数以by value方式调用:
bool validateStudent(Student s);
Student plato;
bool platoIsOk = validateStudent(plato);//以拷贝构造函数的方式调用
调用的复杂度:6次构造函数+6次析构函数!!(包含类的私语成员string对象的构造和析构)
优化---> bool validateStudent(const Student &s);
优化结果:无任何构造和析构被调用。声明const在这里很重要:确保传入的Student不会被改变。
-------------------------------------------------------------------------------------------
假设有一个图形窗口系统:
class Window{
public:
...
std::string name() const;//返回窗口名
virtual void display() const;//显示窗口和其内容
};
class WindowWithScrollBars:public Window {
public:
...
virtual void display() const;// virtual 意味着Window对象和WindowWithScrollBars对象的显示方式不同。(条款34 36)
};
假设希望写个函数打印窗口名称,然后显示该窗口。
void printNameAndDisplay(Window w) //error 参数可能被切割!!
{
std::cout << w.name();
w.display();
}
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);
调用结果是WindowWithScrollBars的所有特化信息被切除。调用的display总是Window的,而非WindowWithScrollBars的!!
解决对象切割问题的方法就是使用pass-by-reference-to-const传值
void printNameAndDisplay(const Window &w) //error 参数可能被切割!!
{
std::cout << w.name();
w.display();
}
-------------------------------------------------------------------------------------------
如果是属于内置类型(例如int),pass-by-value就更加高效一些。同样,STL的迭代器和函数同样适用。
对象小 不等于 拷贝构造函数不昂贵!!许多对象(包含STL容器),内含的对象只比指针多一点,但复制这些对象却需要“复制这些指针所指的所有”。
即使小型对象含有并不昂贵的拷贝构造函数,某些编译器对内置对象和用户自定义对象的态度截然不同。纵使两者拥有相同的底层表述。
举例:某些编译器拒绝将只由一个double组成的对象放入缓存器内,却很乐意在一个正规基础上对光秃秃的doubles那么做。
当这种情况发生,应以by-reference传递。因为编译器然会将指针放入缓存器内。
一般而言, 【内置类型 STL迭代器 和函数对象】以外的其他尽量用pass-by-reference-to-const 替换 pass-by-value。