在C++中,操作重载(Operator Overloading)允许对已有的操作符赋予额外的含义,使得它们可以用于自定义的数据类型。比如,我们可以定义两个对象的加法操作。
类型转换是指将一个类型的实例转换为另一个类型。在C++中,类型转换可以是显式的或隐式的,而类型转换运算符允许控制这个转换过程。
::
, .*
, .
和 ?:
,不能被重载。输入(>>
)和输出(<<
)运算符在C++中用于数据的输入和输出。通常情况下,这些操作符被用于标准的输入输出流(如 std::cin
和 std::cout
)。但是,也可以重载这些运算符以使它们适用于自定义的数据类型。
<<
为了使自定义类型能够通过 std::cout
输出,需要重载 <<
运算符。这通常通过编写一个非成员函数来实现,该函数接受一个输出流(如 std::ostream
)和要输出的对象作为参数。
示例:
class MyClass {
public:
int value;
MyClass(int v) : value(v) {}
};
std::ostream& operator<<(std::ostream& os, const MyClass& obj) {
os << obj.value;
return os;
}
在这个例子中,使用 std::cout << myObject;
时,就会调用我们为 MyClass
类型重载的 <<
运算符。
>>
类似地,为了使自定义类型可以从 std::cin
或其他输入流中接收输入,需要重载 >>
运算符。这也是通过编写一个非成员函数来实现的。
示例:
std::istream& operator>>(std::istream& is, MyClass& obj) {
is >> obj.value;
return is;
}
通过这种方式,可以使用 std::cin >> myObject;
来给 MyClass
类型的对象输入值。
在C++中,不仅可以重载输入输出运算符,还可以重载各种算术和关系运算符,以便在自定义数据类型上实现特定的操作。
算术运算符包括 +
, -
, *
, /
, %
等。通过重载这些运算符,可以定义自定义类型对象间的加法、减法、乘法等操作。
示例:
class Complex {
public:
double real;
double imag;
Complex(double r, double i) : real(r), imag(i) {}
// 重载加法运算符
Complex operator+(const Complex& rhs) const {
return Complex(real + rhs.real, imag + rhs.imag);
}
// 其他运算符...
};
在这个例子中,定义了一个复数类 Complex
并重载了 +
运算符,使其能够实现两个复数的加法。
相等运算符(==
)和不等运算符(!=
)用于比较两个对象是否相等或不等。重载这些运算符时,通常需要确保它们的行为符合逻辑和直觉。
示例:
class MyClass {
public:
int value;
MyClass(int v) : value(v) {}
bool operator==(const MyClass& rhs) const {
return value == rhs.value;
}
bool operator!=(const MyClass& rhs) const {
return !(*this == rhs);
}
};
在这个例子中,MyClass
类型的对象通过比较它们的 value
成员来判断是否相等。
关系运算符包括 <
, >
, <=
, >=
等。它们用于定义对象间的大小比较逻辑。
示例:
bool operator<(const MyClass& lhs, const MyClass& rhs) {
return lhs.value < rhs.value;
}
// 其他关系运算符...
在这个例子中,<
运算符被重载以比较两个 MyClass
类型对象的 value
。
赋值运算符(=
)是C++中非常重要的一个运算符,它用于将一个对象的值赋给另一个对象。对于自定义的数据类型,通常需要重载赋值运算符,以确保对象之间的赋值行为正确。
class MyClass {
public:
int* data;
MyClass(int d) : data(new int(d)) {}
~MyClass() { delete data; }
// 重载赋值运算符
MyClass& operator=(const MyClass& rhs) {
if (this != &rhs) { // 自赋值检查
delete data; // 释放现有资源
data = new int(*rhs.data); // 分配新资源
}
return *this;
}
};
在这个例子中,定义了一个类 MyClass
,它有一个动态分配的整数成员。赋值运算符首先检查自赋值,然后释放当前对象所持有的资源,并从右侧操作数复制数据。
下标运算符([]
)在C++中用于访问对象中的元素,如数组或容器类的元素。对于自定义数据类型,可以重载下标运算符,以提供类似数组一样的访问方式。
class MyArray {
int* array;
int size;
public:
MyArray(int sz) : size(sz), array(new int[sz]) {}
~MyArray() { delete[] array; }
// 非常量版本
int& operator[](int index) {
return array[index];
}
// 常量版本
const int& operator[](int index) const {
return array[index];
}
};
在这个例子中,MyArray
类重载了下标运算符,允许用户通过索引访问数组元素,就像使用普通数组一样。
递增(++
)和递减(--
)运算符在C++中用于增加或减少对象的值。这些运算符可以被重载以适用于自定义数据类型,允许对象以特定方式响应递增或递减操作。
++obj
和 --obj
,先改变对象的值,然后返回改变后的对象。obj++
和 obj--
,先返回对象当前的值,然后改变对象的值。class Counter {
private:
int value;
public:
Counter(int v = 0) : value(v) {}
// 前缀递增
Counter& operator++() {
++value;
return *this;
}
// 后缀递增
Counter operator++(int) {
Counter temp = *this;
++(*this);
return temp;
}
// 类似地,可以实现递减运算符
};
在这个例子中,Counter
类重载了递增运算符。前缀版本直接增加值并返回,而后缀版本则首先创建一个当前状态的副本,然后增加值,最后返回副本。
在C++中,成员访问运算符包括两种:点运算符(.
)和箭头运算符(->
)。点运算符用于访问对象的成员,而箭头运算符用于通过指针访问对象的成员。对于自定义数据类型,尤其是实现了指针类行为的类型,可以重载箭头运算符。
.
点运算符用于直接访问对象的成员。这个运算符不能被重载,因为它总是需要直接访问对象的实际成员。
->
箭头运算符用于通过对象的指针来访问其成员。如果我们创建了类似指针的对象,比如智能指针,就需要重载这个运算符。
template <typename T>
class SmartPointer {
private:
T* ptr;
public:
SmartPointer(T* p = nullptr) : ptr(p) {}
~SmartPointer() { delete ptr; }
T& operator*() { return *ptr; }
T* operator->() { return ptr; }
};
在这个例子中,定义了一个简单的智能指针类 SmartPointer
。通过重载 ->
运算符,可以通过智能指针访问其指向对象的成员,就像使用普通指针一样。
->
运算符必须返回一个对象的指针,或者是另一个定义了 ->
操作的对象。函数调用运算符 ()
在C++中可以被重载,使得一个对象能像函数一样被调用。这种特性主要用于创建可调用的对象,例如函数对象(functors)或者Lambda表达式。
Lambda表达式是C++11引入的一个特性,它允许我们定义匿名函数对象。Lambda可以捕获作用域中的变量,并且可以像普通函数一样被调用。
示例:
auto sum = [](int a, int b) { return a + b; };
std::cout << sum(3, 4); // 输出 7
在这个例子中,我们定义了一个Lambda表达式来求两个数的和。
标准库中提供了许多预定义的函数对象,如 std::plus
、std::minus
等,这些都是重载了函数调用运算符的类。
示例:
std::plus<int> add;
std::cout << add(3, 4); // 输出 7
在这个例子中,我们使用了标准库中的 std::plus
类来进行加法操作。
std::function
std::function
是一个模板类,它可以包裹任何可调用对象,比如普通函数、Lambda表达式、函数对象等。
示例:
std::function<int(int, int)> add = [](int a, int b) { return a + b; };
std::cout << add(3, 4); // 输出 7
在这个例子中,使用 std::function
来存储一个Lambda表达式。
std::function
提供了灵活的方式来处理可调用对象。在C++中,除了重载常用的算术和逻辑运算符外,还可以重载类型转换运算符。这使得我们可以定义对象如何从一种类型转换为另一种类型。
类型转换运算符用于将一个对象隐式或显式地转换为另一种类型。这些运算符的重载可以提供更多的控制,确保类型转换按预期进行。
示例:
class MyClass {
int value;
public:
MyClass(int v) : value(v) {}
// 转换为int类型的运算符
operator int() const {
return value;
}
};
在这个例子中,MyClass
对象可以隐式地转换为 int
类型。
类型转换应该明确无误。如果一个类提供了多个可能的类型转换,可能会导致二义性,这应该尽量避免。
在重载运算符时,应该注意函数匹配的问题。重载应该清晰而明确,不应该引起调用者的困惑或误解。
explicit
)可以用来防止隐式类型转换,确保转换行为明确。