• Index

重载运算符

Reads: 16

使用重载运算符

基础示例 1

现在尝试实现text * 2这样的操作,使得运算得到结果你好你好。其中textstd::u32string类型的字符串对象,并且保存的值是你好。也就是"你好" * 2"你好你好"

#include <iostream> // std::cout std::endl
#include <string> // std::string

std::string operator*(const std::string &text, unsigned long long n);

int main(void)
{
    std::string text = "你好";
    std::cout << "你好 × 0:" << (text * 0) << std::endl;
    std::cout << "你好 × 1:" << (text * 1) << std::endl;
    std::cout << "你好 × 2:" << (text * 2) << std::endl;
    std::cout << "你好 × 3:" << (text * 3) << std::endl;
    std::cout << "你好 × 4:" << (text * 4) << std::endl;
    return 0;
}

std::string operator*(const std::string &text, unsigned long long n)
{
    if (n == 0)
    {
        return std::string();
    }
    std::string result;
    for (unsigned long long i = 0; i < n; ++i)
    {
        result += text;
    }
    return result;
}

输出结果:

你好 × 0:
你好 × 1:你好
你好 × 2:你好你好
你好 × 3:你好你好你好
你好 × 4:你好你好你好你好

基础讲解 1

*是一个运算符,现在给*一个新的功能,就是使它能够用在上面这种情况。从代码可以看出,需要以函数的形式,才能给运算符添加新功能。由于*已经有如数学乘法等其他功能,所以要用一个新的函数重新定义它的功能,这是我们熟悉的重载函数;而且由于重载的是运算符,所以应该叫做重载运算符函数

重载运算符函数的函数名称:关键字operator + 需要重载的运算符,如上面的operator*

参数和返回值都需要看情况。如上面的*,由于乘法肯定是需要两个参数,所以operator*必须有两个参数。至于参数和返回值的类型就要按实际情况,因为我需要上面的功能,所以第一个参数是std::string,而第二个参数就是无符号整数,返回值也是std::string

当使用text * 2时,*左边的值作为operator*的第一个参数,*右边的值作为operator*的第二个参数,然后根据这两个参数选择适当的重载函数。也因此,假如我写成了2 * text,那么,由于没有适当的重载函数,所以会导致编译报错。

至于重载函数operator*()的定义就不需要过多地解释了,相信你会看得懂。

温馨提示:重载函数operator*()中,std::string result;声明后,如果马上使用成员函数reserve()来指明字符串的字节数,那么就可以提高运行性能。

基础拓展 1

除了使用*外,实际上也可以直接使用它的函数名称,例如下面用法:

operator*(text, 2);

上面这行代码跟下面这行代码等价:

text * 2;

基础示例 2

二维坐标点point_t的对象乘以2,使对象的成员变量xy分别乘以2。

#include <iostream> // std::cout std::endl

class point_t
{
public:
    point_t(int a, int b);
    point_t operator*(int n);
public:
    int x;
    int y;
};

int main(void)
{
    point_t point1(5, 6);
    auto point2 = point1 * 5;
    std::cout << "point1横坐标的值是:" << point1.x << std::endl;
    std::cout << "point1纵坐标的值是:" << point1.y << std::endl;
    std::cout << "point2横坐标的值是:" << point2.x << std::endl;
    std::cout << "point2纵坐标的值是:" << point2.y << std::endl;
    return 0;
}

point_t::point_t(int a, int b)
    : x(a)
    , y(b)
{
}

point_t point_t::operator*(int n)
{
    return point_t(x * n, y * n);
}

输出结果:

point1横坐标的值是:5
point1纵坐标的值是:6
point2横坐标的值是:25
point2纵坐标的值是:30

基础讲解 2

从代码中可以看出,重载运算符除了可以定义成全局函数外,也可以定义成类成员函数。

当重载运算符函数作为类成员函数时,只需要一个参数。当使用point1 * 5时,就会调用类的重载运算符函数,并且将*右边的值作为参数。同样,如果写成了5 * point1,那么编译器就会找不到对应重载函数而报错,如果需要5 * point1这样的功能,那么可以再写一个对应的重载函数。

基础拓展 2

除了使用*外,实际上也可以直接使用它的成员函数名称,例如下面用法:

point1.operator*(5);

上面这行代码跟下面这行代码等价:

point1 * 5;

可重载的运算符

可以重载的符号只有下列这38个:+ - * / % ˆ & | ~ ! = < > += -= *= /= %= ˆ= &= |= << >> >>= <<= == != <= >= && || ++ -- , ->* -> () []

其中,= () [] ->只能作为成员函数,而其他运算符既可以作为成员函数来重载,也可以作为普通函数来重载。

举个例子,假设表达式a + b;,如果+是某类的成员函数而且a是该类的对象,那么a + b;就可以写成a.operator+(b);,它们是等价的,这个时候成员函数operator+只能接收一个参数;而如果运算符+是普通函数,那么a + b;就可以写成operator+(a, b);,它们是等价的,这个时候成员函数operator+只能接收两个参数。其他运算符也是同理。

比较特殊的运算符是++--,因为它们有前置和后置两种,这里以++作为成员函数为例子说明。假设a是类testclass的对象,那么++a;的函数声明就是testclass & operator++(void);,而a++;的函数声明就是testclass operator++(int);。作为区别,规定前置++的重载函数是没有参数的;而后置++的重载函数是只有一个参数而且必须是int类型的,由于这个参数只是用于区分,所以使用这个参数的值是没有意义的。至于返回值是没有严格规定的,但是按照惯例,前置++返回该对象的引用或者返回void,而后置++则返回该对象的复制,所以使用前置++一般会比后置++

在类中可以使用这样的成员函数operator 类型(void);,如operator bool(void);,这种函数叫做转换函数。顾名思义,当一个类定义了转换函数,那么这个类的对象都可以转换成定义的类型。假设类testclass定义了转换函数operator bool(void);,那么testclass的对象a就可以这样玩bool b = static_cast<bool>(a);或者这样bool b = a;;而如果强制调用者必须显式转换,那么转换函数的声明可以这样写explicit operator bool(void);,那么这时候只能使用bool b = static_cast<bool>(a);。当然啦,转换函数是自己定义的,那么就要返回自己定义的类型的返回值。

关键字newdeletenew []delete []这四个也是运算符,也可以被重载。重载operator newoperator new []的参数可以任意数量,但第一个参数必须是std::size_t类型,返回值必须是void *,即void * operator new(std::size_t size);;而重载operator deleteoperator delete [],必须只有一个参数,而且必须是void *类型,而且返回类型必须是void,即void operator delete(void *p);

巩固练习

现在为simple_vector完成以下成员函数:

// 查看指定位置的值
// 参数: pos 索引位置, 从0开始
// 返回值: 指定位置的值
const int & operator[](std::size_t pos) const;

// 查看指定位置的值
// 参数: pos 索引位置, 从0开始
// 返回值: 指定位置的值
int & operator[](std::size_t pos);

提示

重载符号[]和成员函数at()没有什么区别,就是少了越界的检测。而实际上std::vectorstd::string等,重载的[]都不检测边界,需要调用者保证不会错误,而成员函数at()都检测边界,如果超出范围则抛出异常。


Comments

Make a comment

  • Index

WARNING: You are using an old browser that does not support HTML5. Please choose a modern browser (Chrome / Microsoft Edge / Firefox / Sarafi) to get a good experience.