static_cast基本用法
static_cast 是 C++ 中四个命名强制类型转换操作符之一。它用于执行各种不同类型之间的转换
1. 使用场景
1.1 基础数据类型的转换
可以将一种基础数据类型转换为另一种基础数据类型。例如,将 double 转换为 int,或将 float 转换为 double 等。
double d = 5.5;
int i = static_cast<int>(d); // i = 5
1.2 指向派生类的指针或引用转换为指向基类的指针或引用
class Base {};
class Derived : public Base {};
Derived derivedObj;
Base* basePtr = static_cast<Base*>(&derivedObj);
1.3 指向基类的指针或引用转换为指向派生类的指针或引用
但这是不安全的,因为在转换过程中没有运行时检查。如果确实需要运行时检查,应使用 dynamic_cast
Base* basePtr = new Base();
Derived* derivedPtr = static_cast<Derived*>(basePtr); // 不安全!
1.4 在有关联的类型之间进行转换
例如,转换枚举值为整数
enum Color { RED, GREEN, BLUE };
int value = static_cast<int>(GREEN); // value = 1
static_cast用法
static_cast<int&&> 是一种将类型转换为右值引用
那么什么是左值和右值呢?
左值 (Lvalue):
- 可以通过名字引用的对象(如变量)
- 有一个确定的存储位置
- 可以出现在赋值号的左边
int x = 10; // x 是左值
x = 20; // 可以给左值赋值
右值 (Rvalue):
- 通常是一个临时对象,存储在寄存器或堆栈中,而不是具名变量
- 不能出现在赋值号的左边
int y = x + 5; // x + 5 是右值
左值传递
当一个函数接收参数时,左值传递意味着参数可以直接用左值绑定到函数的形参。通常,左值传递通过引用(&)实现,避免拷贝
void print(int& value) {
std::cout << "Left value: " << value << std::endl;
value = 5;
}
int x = 10;
print(x); // x 是左值,这里直接传递左值引用
std::cout << "Left value: " << x << std::endl;
这里会先后输出 10 和 5,首先需要知道的一点,在初学C的时候,我们就知道,变量传入函数里面是不能修改赋值的,只有指针会修改赋值
这是因为在传入参数的时候,会出现形参的概念,也就是传承会被赋值一份,至于复制的过程,涉及寄存器、汇编相关知识,这里不展开
只需要知道传进去的值是被复制的,不是原先的变量,这个时候我们使用 & 可以实现左值的绑定,这个时候就不会再复制形参了
意味着我们传入的值一旦修改就是真的修改了,如果你不希望修改,可以写 const int& value ,当然如果没有需求,尽量不使用左值传递
右值引用
右值引用(T&&) 是 C++11 引入的新特性,用来接收右值参数
与左值引用不同,右值引用可以绑定到右值(临时对象)上,从而实现高效的资源转移(移动语义)
右值引用的主要用途:
支持移动语义:减少拷贝,提高性能
区分左值和右值:编写重载函数,针对右值进行特殊处理
深拷贝是一种完全复制对象内容的方式,特别是当对象内部有动态分配的资源时,需要确保新对象与原对象之间资源相互独立
std::move()函数的使用方法写在单独的一小节,可以先看使用方法再接下来往下看
1. 实现移动语义
右值引用的最重要用途是支持移动语义,避免昂贵的深拷贝
noexcept 该关键字告诉编译器,函数中不会发生异常,这有利于编译器对程序做更多的优化
#include <iostream>
#include <vector>
class MyClass {
public:
std::vector<int> data;
// 默认构造函数
MyClass() { std::cout << "Default constructor\n"; }
// 拷贝构造函数
MyClass(const MyClass& other) : data(other.data) {
std::cout << "Copy constructor\n";
}
// 移动构造函数
MyClass(MyClass&& other) noexcept : data(std::move(other.data)) {
std::cout << "Move constructor\n";
}
// 拷贝赋值运算符
MyClass& operator=(const MyClass& other) {
std::cout << "Copy assignment\n";
data = other.data;
return *this;
}
// 移动赋值运算符
MyClass& operator=(MyClass&& other) noexcept {
std::cout << "Move assignment\n";
data = std::move(other.data);
return *this;
}
};
int main() {
MyClass a;
a.data = {1, 2, 3};
MyClass b(a);
MyClass c = std::move(a); // 调用移动构造函数
MyClass d;
d = std::move(c); // 调用移动赋值运算符
}
2. 重载函数来区分左值和右值
右值引用可以用来重载函数,根据参数是左值还是右值来执行不同的逻辑
#include <iostream>
void process(const std::string& s) {
std::cout << "Process left-value: " << s << '\n';
}
void process(std::string&& s) {
std::cout << "Process right-value: " << s << '\n';
}
int main() {
std::string str = "Hello";
process(str); // 调用左值重载
process(std::move(str)); // 调用右值重载
process("World"); // 调用右值重载
}
输出:
Process left-value: Hello
Process right-value: Hello
Process right-value: World
3. 实现通用引用
右值引用配合模板可以实现通用引用,用于完美转发
#include <iostream>
#include <utility>
void process(int& x) {
std::cout << "Left-value reference: " << x << '\n';
}
void process(int&& x) {
std::cout << "Right-value reference: " << x << '\n';
}
template <typename T>
void forwardToProcess(T&& t) {
process(std::forward<T>(t));
}
int main() {
int a = 42;
forwardToProcess(a); // 传递左值
forwardToProcess(42); // 传递右值
forwardToProcess(std::move(a));// 强制作为右值传递
}
输出:
Left-value reference: 42
Right-value reference: 42
Right-value reference: 42
4.返回右值引用
右值引用可以作为返回值类型,允许返回局部对象的资源(通过移动语义)
#include <iostream>
#include <vector>
std::vector<int> createVector() {
std::vector<int> vec = {1, 2, 3};
return std::move(temp); // 直接移动 temp 的资源
}
int main() {
std::vector<int> v = createVector(); // 触发移动语义
for (int x : v) {
std::cout << x << " ";
}
}
5. 高效的容器操作
右值引用常用于标准容器(如 std::vector),使插入和删除操作更加高效
示例:使用右值引用避免拷贝
#include <iostream>
#include <vector>
int main() {
std::vector<std::string> vec;
std::string str = "Hello";
vec.push_back(str); // 拷贝插入
vec.push_back(std::move(str)); // 移动插入
for (const auto& s : vec) {
std::cout << s << " ";
}
}
输出:
Hello Hello