重载括号运算符
到目前为止,您看到的所有重载运算符都允许您定义运算符参数的类型,但不能定义参数的数量(运算符的类型是固定的)。例如,operator ==总是需要两个参数,而operator!总是拿一个。括号运算符(operator())是一个特别有趣的运算符,它允许您改变所需参数的类型和数量。
要记住两件事:首先,括号运算符必须实现为成员函数。其次,在非面向对象的C ++中,()运算符用于调用函数。对于类,operator()只是一个普通的运算符,它调用一个函数(名为operator()),就像任何其他重载运算符一样。
让我们看一个有助于重载此运算符的示例:
class Matrix
{
private:
double data[4][4];
public:
Matrix()
{
// 将matrix所有元素设置为 0.0
for (int row=0; row < 4; ++row)
for (int col=0; col < 4; ++col)
data[row][col] = 0.0;
}
};
矩阵是线性代数的关键组成部分,通常用于进行几何建模和3D计算机图形处理。在这种情况下,您需要识别的是Matrix类是一个4乘4的二维二维数组。
在重载下标运算符的课程中,您了解到我们可以重载operator []以提供对私有一维数组的直接访问。但是,在这种情况下,我们希望访问私有二维数组。因为operator []仅限于一个参数,所以让我们索引一个二维数组是不够的。
但是,因为()运算符可以使用我们想要的参数,我们可以声明一个带有两个整数索引参数的operator()版本,并使用它来访问我们的二维数组。这是一个例子:
#include <cassert> // assert()
class Matrix
{
private:
double data[4][4];
public:
Matrix()
{
// 将matrix所有元素设置为 0.0
for (int row=0; row < 4; ++row)
for (int col=0; col < 4; ++col)
data[row][col] = 0.0;
}
double& operator()(int row, int col);
const double& operator()(int row, int col) const; // 为const对象
};
double& Matrix::operator()(int row, int col)
{
assert(col >= 0 && col < 4);
assert(row >= 0 && row < 4);
return data[row][col];
}
const double& Matrix::operator()(int row, int col) const
{
assert(col >= 0 && col < 4);
assert(row >= 0 && row < 4);
return data[row][col];
}
现在我们可以声明一个Matrix并访问它的元素,如下所示:
#include <iostream>
int main()
{
Matrix matrix;
matrix(1, 2) = 4.5;
std::cout << matrix(1, 2);
return 0;
}
产生结果:
4.5
现在,让我们再次重载()运算符,这次不采用任何参数:
#include <cassert> // assert()
class Matrix
{
private:
double data[4][4];
public:
Matrix()
{
// 将matrix所有元素设置为 0.0
for (int row=0; row < 4; ++row)
for (int col=0; col < 4; ++col)
data[row][col] = 0.0;
}
double& operator()(int row, int col);
const double& operator()(int row, int col) const;
void operator()();
};
double& Matrix::operator()(int row, int col)
{
assert(col >= 0 && col < 4);
assert(row >= 0 && row < 4);
return data[row][col];
}
const double& Matrix::operator()(int row, int col) const
{
assert(col >= 0 && col < 4);
assert(row >= 0 && row < 4);
return data[row][col];
}
void Matrix::operator()()
{
// 将matrix所有元素重置为 0.0
for (int row=0; row < 4; ++row)
for (int col=0; col < 4; ++col)
data[row][col] = 0.0;
}
这是我们的新例子:
#include <iostream>
int main()
{
Matrix matrix;
matrix(1, 2) = 4.5;
matrix(); // 重置
std::cout << matrix(1, 2);
return 0;
}
产生结果:
0
因为()运算符非常灵活,所以很有可能将它用于许多不同的目的。但是,强烈建议不要这样做,因为()符号实际上并未表明程序员正在做什么。在上面的例子中,最好将擦除功能写成一个名为clear()或erase()的函数,这matrix.erase()比matrix()(它可以做任何事情!)更容易理解。
享受下面这个函数带给你的乐趣
Operator()通常也会重载以实现仿函数(或函数对象),这些函数就像函数一样运行。仿函数优于普通函数的优点是仿函数可以将数据存储在成员变量中(因为它们是类)。
这是一个简单的仿函数:
class Accumulator
{
private:
int m_counter = 0;
public:
Accumulator()
{
}
int operator() (int i) { return (m_counter += i); }
};
int main()
{
Accumulator acc;
std::cout << acc(10) << std::endl; //打印10
std::cout << acc(20) << std::endl; // 打印30
return 0;
}
注意,使用我们的acc看起来就像进行正常的函数调用,但我们的Accumulator对象存储累积值。
你可能想知道为什么我们不能用普通函数和静态局部变量做同样的事情来保存函数调用之间的数据。我们可以,但因为函数只有一个全局实例,所以我们一次只能将它用于一件事。使用仿函数,我们可以根据需要实例化多个独立的仿函数对象,并同时使用它们。
Conclusion:
Operator()有时会重载两个参数来索引多维数组,或者检索一维数组的子集(两个参数定义要返回的子集)。其他任何东西都可能更好地写成具有更具描述性名称的成员函数。
Operator()也经常被重载以创建仿函数。虽然简单的仿函数(例如上面的示例)相当容易理解,但仿函数通常用于更高级的编程主题,并且值得我们自己的学习。
Quiz Time:
1)写一个包含字符串的类。重载operator()以返回从第一个参数的索引处开始的子字符串。子字符串的长度应由第二个参数定义。
提示:您可以使用数组索引来访问std :: string中的各个字符
提示:您可以使用operator + =将某些内容附加到字符串
应运行以下代码:
int main()
{
Mystring string("Hello, world!");
std::cout << string(7, 5); // 从下标7开始 并且返回5个元素
return 0;
}
这应该打印
world
解决方案
#include <string>
#include <iostream>
#include <cassert>
class Mystring
{
private:
std::string m_string;
public:
Mystring(const std::string string="")
:m_string(string)
{
}
std::string operator()(int start, int length)
{
assert(start + length <= m_string.length() && "Mystring::operator(int, int): Substring is out of range");
std::string ret;
for (int count = 0; count < length; ++count)
ret += m_string[start + count];
return ret;
}
};
int main()
{
Mystring string("Hello, world!");
std::cout << string(7, 5); //从下标7开始 并且返回5个元素
return 0;
}