.
在C++中,函数参数与返回值的数据传递的方式,对程序的性能和正确性有着重要影响。C++默认使用pass-by-value(传值)的方式传递参数。但这种方式在某些情况下会导致性能问题和对象切割问题。
C++推荐使用pass-by-reference-to-const(传常量引用)的方式来传递参数。
1、C++默认传值方式的问题
1.1、多次调用拷贝构造函数
- 当使用pass-by-value传递参数时,C++会调用参数的拷贝构造函数来创建一个副本。如果传递的是一个复杂的对象(例如包含大量数据的类对象),拷贝构造函数的调用会导致性能问题,尤其是在频繁调用的情况下。
#include <iostream>
#include <string>class MyClass {
public:MyClass() {std::cout << "Default Constructor" << std::endl;}MyClass(const MyClass& other) {std::cout << "Copy Constructor" << std::endl;}~MyClass() {std::cout << "Destructor" << std::endl;}
};void func(MyClass obj) {// 使用obj
}int main() {MyClass obj;func(obj); // 调用拷贝构造函数return 0;
}输出:
Default Constructor
Copy Constructor
Destructor
Destructor
1.2、对象切割问题
- 对象切割问题发生在传递派生类对象给基类参数时。如果使用pass-by-value传递参数,派生类会被切割成基类对象,丢失派生类的特有部分。
- 原因:当函数参数类型是基类时,编译器会调用基类的拷贝构造函数来创建基类对象的副本。即使基类有虚函数,基类对象的虚表指针会被重置为指向基类的虚表,导致派生类的虚函数无法被调用。
#include <iostream>class Base {
public:virtual void print() const {std::cout << "Base" << std::endl;}
};class Derived : public Base {
public:void print() const override {std::cout << "Derived" << std::endl;}
};void func(Base obj) {obj.print(); // 总是调用Base的print
}int main() {Derived d;func(d); // 对象切割,丢失Derived部分return 0;
}输出:
Base
2、使用pass-by-reference-to-const替换pass-by-value
- 为了避免上述问题,C++体检使用pass-by-reference-to-const来传递参数,避免不必要的拷贝和对象切割问题
- 原理:在底层实现上,引用是通过指针来实现的,编译器会将引用转换成指针操作,传递的是对象地址。
- 指针大小是固定的4字节,无论对象有多大,传递引用的开销非常小。使用const常量修饰可避免对象修改。
- 因为传值引用对象直接指向原始对象,不会创建新的基类对象。
class Base
{
public:virtual void print() const{std::cout << "Base" << std::endl;}
};class Derived : public Base
{
public:void print() const override{std::cout << "Derived" << std::endl;}
};void func(const Base &obj)
{obj.print(); // 总是调用Base的print
}int main()
{Derived d;func(d); // 对象切割,丢失Derived部分return 0;
}输出:
Derived
3、适合pass-by-value的情况
- 内置类型:对于内置类型(如int、double、char等),使用pass-by-value通常更高效,因为拷贝这些类型的开销很小。
- 小型对象:对于小型对象(如std::pair、std::tuple等),使用pass-by-value也可能更高效。
#include <iostream>void func(int x) {std::cout << "Value: " << x << std::endl;
}int main() {int a = 10;func(a); // 对于内置类型,使用pass-by-valuereturn 0;
}
思维导图笔记: