📜  C++ 中不同引用的重载(1)

📅  最后修改于: 2023-12-03 14:39:54.333000             🧑  作者: Mango

C++ 中不同引用的重载

在 C++ 中,有多种类型的引用,如左值引用、右值引用和常量引用。对这些引用进行函数重载时,需要注意它们的区别以及各自的使用情境。

左值引用

左值引用是最基础的引用类型,通过 & 符号进行声明。它可以绑定到左值(表达式、变量等可以取地址且具有持久性的数据),也可以绑定到常量左值,但不能绑定到右值。

int n = 42;
int &ref_n = n;       // 正确,ref_n 是 n 的左值引用
const int &ref_c_n = n;  // 正确,ref_c_n 是 n 的常量左值引用
int &ref2 = 42;       // 错误,42 是右值

以下是一个函数重载的例子,其中重载函数 modify 接受一个整数的左值引用,可以修改其值。另一个重载函数 print 接受一个整数的常量左值引用,只用于输出。

void modify(int &n) {
    n++;
}

void print(const int &n) {
    cout << n << endl;
}

int main() {
    int x = 42;
    modify(x);    // x 的值变为 43
    print(x);     // 输出 43
    print(42);    // 对于常量左值引用,也可以接受右值,输出 42
    return 0;
}
右值引用

右值引用是 C++11 新增的引用类型,通过 && 符号进行声明。它可以绑定到右值(表达式、临时对象等具有零时性的数据),但不能绑定到左值。

int n = 42;
int &&ref_r_n = n + 1;  // 正确,n + 1 是右值
int &&ref2 = n;         // 错误,n 是左值

右值引用最常见的用途是移动语义,即将一个对象的资源(如内存区域)移动到另一个对象,避免拷贝导致的性能损失。

以下是一个利用右值引用实现移动语义的例子。我们定义了一个简单的类 MyVector,其中包含一个指向动态数组的指针。在复制构造函数和赋值运算符中,我们利用右值引用来实现资源的移动。

class MyVector {
public:
    MyVector() { p = nullptr; }
    MyVector(int n) {
        p = new int[n];
        for (int i = 0; i < n; i++) {
            p[i] = 0;
        }
    }
    MyVector(const MyVector &rhs) {  // 复制构造函数
        p = nullptr;
        if (rhs.p != nullptr) {
            p = rhs.p;
            rhs.p = nullptr;
        }
    }
    MyVector &operator=(MyVector &&rhs) {  // 赋值运算符
        if (p != rhs.p) {
            delete[] p;
            p = rhs.p;
            rhs.p = nullptr;
        }
        return *this;
    }
    ~MyVector() { delete[] p; }
private:
    int *p;
};

int main() {
    MyVector v1(10), v2;
    v2 = std::move(v1);  // 调用赋值运算符,v2 获得 v1 的资源
    return 0;
}
常量引用

常量引用是指对于一个常量对象或临时对象的引用,通过 const& 符号进行声明。它不能被用于修改对象的值,而只能用于读取。

int n = 42;
const int &ref_c_n = n;  // 正确,ref_c_n 是 n 的常量左值引用
const int &ref2 = 42;    // 也可以绑定到一个右值
int &ref3 = ref_c_n;     // 错误,不能用一个非常量引用绑定到一个常量引用

以下是一个函数重载的例子,其中重载函数 print 既可以接受一个整数的左值引用,也可以接受一个整数的常量引用。对于常量引用的情况,不能修改其值。

void print(int &n) {
    n++;
    cout << n << endl;
}

void print(const int &n) {
    cout << n << endl;
}

int main() {
    int x = 42;
    int &ref_n = x;
    const int &ref_c_n = x;
    print(ref_n);      // x 的值变为 43,输出 43
    print(ref_c_n);    // 输出 42
    print(42);         // 对于常量引用,也可以接受右值,输出 42
    return 0;
}
总结

对于不同类型的引用,在函数重载时应该使用合适的引用类型进行参数声明。左值引用可以用于修改对象内容,右值引用可以用于实现资源的移动,常量引用可以方便地读取常量对象或临时对象。合理地使用不同类型的引用,可以避免不必要的复制、提高性能并增强程序的可读性。