【知识点】 C++ 构造函数 & 参数类型为右值引用的模板函数

C++ 构造函数是一种特殊的成员函数,用于初始化类对象。C++ 中的构造函数主要分为以下几种类型:

  1. 默认构造函数(Default Constructor)
  2. 参数化构造函数(Parameterized Constructor)
  3. 拷贝构造函数(Copy Constructor)
  4. 移动构造函数(Move Constructor)
  5. 委托构造函数(Delegating Constructor)

1. 默认构造函数(Default Constructor)

默认构造函数是在没有提供参数的情况下调用的构造函数。如果程序员没有定义构造函数,编译器会生成一个隐式默认构造函数。程序员也可以显式定义默认构造函数。

示例
class MyClass {
public:
    MyClass() {
        std::cout << "Default Constructor called" << std::endl;
    }
};

int main() {
    MyClass obj; // 调用默认构造函数
    return 0;
}

2. 参数化构造函数(Parameterized Constructor)

参数化构造函数允许在创建对象时传递参数,以便初始化对象的成员。

示例
class MyClass {
private:
    int value;
public:
    MyClass(int v) : value(v) {
        std::cout << "Parameterized Constructor called with value: " << value << std::endl;
    }
};

int main() {
    MyClass obj(10); // 调用参数化构造函数
    return 0;
}

3. 拷贝构造函数(Copy Constructor)

拷贝构造函数用于创建一个新的对象,并用现有对象初始化它。其参数是现有对象的引用,通常是 const 引用。

示例
class MyClass {
private:
    int value;
public:
    MyClass(int v) : value(v) {
        std::cout << "Parameterized Constructor called with value: " << value << std::endl;
    }
    
    MyClass(const MyClass& other) : value(other.value) {
        std::cout << "Copy Constructor called" << std::endl;
    }
};

int main() {
    MyClass obj1(10);       // 调用参数化构造函数
    MyClass obj2 = obj1;    // 调用拷贝构造函数
    return 0;
}

4. 移动构造函数(Move Constructor)

移动构造函数用于接管另一个对象的资源,而不是拷贝其值。这对于优化性能和避免不必要的拷贝操作特别有用。移动构造函数的参数是一个右值引用

示例
#include <utility> // std::move

class MyClass {
private:
    int* value;
public:
    MyClass(int v) : value(new int(v)) {
        std::cout << "Parameterized Constructor called with value: " << *value << std::endl;
    }
    
    ~MyClass() {
        delete value;
    }
    
    MyClass(const MyClass& other) : value(new int(*other.value)) {
        std::cout << "Copy Constructor called" << std::endl;
    }
    
    MyClass(MyClass&& other) noexcept : value(other.value) {
        other.value = nullptr;
        std::cout << "Move Constructor called" << std::endl;
    }
};

int main() {
    MyClass obj1(10);       
    MyClass obj2 = std::move(obj1); // 调用移动构造函数
    return 0;
}

参数类型为右值引用的模板函数在 C++ 中用于实现完美转发(perfect forwarding)和移动语义(move semantics)。右值引用(T&&)和模板结合使用,可以创建灵活、高效的代码。

右值引用与模板的结合

在模板中使用右值引用,可以通过类型推导实现完美转发。完美转发是指将参数完整地传递给另一个函数,保持参数的原始类型(左值或右值)和属性。

示例:右值引用的模板函数

基本模板函数

下面是一个接受右值引用参数的模板函数示例:

#include <iostream>
#include <utility> // std::forward

template<typename T>
void process(T&& arg) {
    std::cout << "Processing" << std::endl;
    // 使用 arg
}

int main() {
    int x = 10;
    process(x);          // x 是左值
    process(20);         // 20 是右值
    process(std::move(x)); // std::move(x) 是右值
    return 0;
}

完美转发

为了实现完美转发,使用 std::forward 函数。std::forward 会根据参数的实际类型(左值或右值)进行转发。

示例:完美转发
#include <iostream>
#include <utility> // std::forward

void print(int& t) {
    std::cout << "Lvalue: " << t << std::endl;
}

void print(int&& t) {
    std::cout << "Rvalue: " << t << std::endl;
}

template<typename T>
void forwarder(T&& arg) {
    print(std::forward<T>(arg)); // 完美转发
}

int main() {
    int x = 10;
    forwarder(x);           // 转发左值
    forwarder(20);          // 转发右值
    forwarder(std::move(x)); // 转发右值
    return 0;
}

在这个示例中,forwarder 函数使用 std::forward 将参数 arg 转发给 print 函数。std::forward<T>(arg) 保留了参数的类型属性,确保 print 函数根据参数的实际类型调用正确的重载版本。

深入理解 T&& 在模板中的行为

在模板中,T&& 表示万能引用(universal reference),即它可以绑定到左值或右值。如果你传递的是左值,那么 T 推导为左值引用类型。如果你传递的是右值,那么 T 推导为非引用类型。

示例
#include <iostream>
#include <type_traits>

template<typename T>
void check(T&& arg) {
    if (std::is_lvalue_reference<T>::value) {
        std::cout << "T is lvalue reference" << std::endl;
    } else {
        std::cout << "T is not lvalue reference" << std::endl;
    }

    if (std::is_rvalue_reference<decltype(arg)>::value) {
        std::cout << "arg is rvalue reference" << std::endl;
    } else {
        std::cout << "arg is not rvalue reference" << std::endl;
    }
}

int main() {
    int x = 10;
    check(x);          // 左值传递
    check(20);         // 右值传递
    check(std::move(x)); // 右值传递
    return 0;
}

输出结果:

T is lvalue reference
arg is not rvalue reference
T is not lvalue reference
arg is rvalue reference
T is not lvalue reference
arg is rvalue reference

从输出结果可以看出,当 x 是左值时,T 被推导为 int&(左值引用),而当 20std::move(x) 是右值时,T 被推导为 int(非引用类型)。

  • 右值引用与模板结合T&& 在模板中被称为万能引用,可以绑定左值或右值。
  • 完美转发:使用 std::forward<T>(arg) 实现完美转发,保持参数的类型属性
  • 万能引用的类型推导T&& 可以根据传递参数的类型(左值或右值)进行不同的类型推导。

参考:从4行代码看右值引用

5. 委托构造函数(Delegating Constructor)

委托构造函数是在同一个类中,一个构造函数调用另一个构造函数,以避免重复代码和简化初始化过程。

示例
class MyClass {
private:
    int value1;
    int value2;
public:
    MyClass(int v1, int v2) : value1(v1), value2(v2) {
        std::cout << "Constructor with two parameters called" << std::endl;
    }

    MyClass(int v) : MyClass(v, 0) {
        std::cout << "Delegating Constructor called" << std::endl;
    }

    MyClass() : MyClass(0, 0) {
        std::cout << "Default Delegating Constructor called" << std::endl;
    }
};

int main() {
    MyClass obj1;       // 调用委托的默认构造函数
    MyClass obj2(10);   // 调用委托的单参数构造函数
    MyClass obj3(10, 20); // 调用带两个参数的构造函数
    return 0;
}

总结

  • 默认构造函数:无参数构造函数,可以由编译器生成或显式定义。
  • 参数化构造函数:带有参数的构造函数,用于初始化对象时传递参数。
  • 拷贝构造函数:使用现有对象初始化新对象的构造函数,参数是对象的 const 引用。
  • 移动构造函数:接管另一个对象的资源,参数是对象的右值引用。
  • 委托构造函数:一个构造函数调用另一个构造函数,以避免代码重复和简化初始化。
  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值