C++ 类和对象 运算符重载

C++类和对象中运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

场景描述:对于内置的数据类型,例如整型、浮点型等,编译器都知道如何进行运算。但是对于一些自定义的数据类型,编译器不知道如何进行运算。

需要解决的问题:因此需要我们对一些运算符进行重载,赋予这些运算符另种功能实现对自定义数据类型的运算。以下主要展示加号、左移、递增、赋值、关系以及函数调用运算符的重载。

加号运算符重载

  1. 作用:实现两个自定义数据类型相加的运算

  1. 场景描述:对于内置的数据类型,编译器都知道如何进行运算,例如:

int a = 10;
int b = 10;
int c = a + b;//编译器可以算出c是多少

但是,对于如下场景,例如

class Person
{
public:
    Person(int a, int b):mA(a), mB(b)
    { }
    int mA;
    int mB;
};

void test1()
{
    Person p1(10, 10);
    Person p2(20, 20);
    //Person p3 = p1 + p2;
}

如果我想实现,“Person p3 = p1 + p2;”,也就是p3.mA=p2.mA+p1.mA,p3.mB=p2.mB+p1.mB。此时需要重新定义加号运算符,即实现加号运算符重载。解决办法:自己写函数来实现。

  1. 思路过程写在图片里

总结起来就是编译器起了通用的名字“operator+”,实现重载+,可以通过成员函数实现,也可以通过全局函数实现。

  1. operator+

自己写的成员函数通过引用的方式传递,返回本身,需要在函数调用前创建一个新对象。对比老师写的,老师通过值传递调用拷贝构造函数做值拷贝,返回在成员函数创建的新对象。这个地方函数名是在讲到operator+出现之前写的,没有改名字。

//Person类的成员函数——自己写的,我的是返回本身
Person& PersonAddP(Person& p1, Person& p2)
    {
        this->mA = p1.mA + p2.mA;
        this->mB = p1.mB + p2.mB;
        return *this;
    }
//老师教例展示的——老师通过值传递调用拷贝函数返回一个新的对象temp
Person PersonAddPerson(Person& p)
    {
        Person temp;
        temp.mA = this->mA + p.mA;
        temp.mB = this->mB + p.mB;
        return temp;
    }
//自己写的测试
void test1()
{
    Person p1(10, 10);
    Person p2(20, 20);
    Person p3(0, 0);
    p3.PersonAddP(p1, p2);
    cout << "p3.mA = " << p3.mA << "\tp3.mB = " << p3.mB << endl;
}

上面展示的例子可以看出,不同的人取不同名字的成员函数。那么为了优化,编译器起了一个通用的成员函数名叫“operator+”,这个成员函数用来实现新数据类型的数据可以进行加号运算。

  1. 整体实现

#include <iostream>
using namespace std;

class Person
{
public:
    //1.成员函数重载+
    /*Person operator+(Person& p)
    {
        Person temp;
        temp.mA = p.mA + this->mA;
        temp.mB = p.mB + this->mB;
        return temp;
    }*/
    int mA;
    int mB;

};

//2.全局函数
Person operator+(Person p1, Person p2)
{
    Person temp;
    temp.mA = p1.mA + p2.mA;
    temp.mB = p1.mB + p2.mB;
    return temp;
}

//3.运算符重载 也可以发生函数重载
Person operator+(Person& p, int num)
{
    Person temp;
    temp.mA = p.mA + num;
    temp.mB = p.mB + num;
    return temp;
}

//重载+
void test2()
{
    Person p1;
    p1.mA = 10;
    p1.mB = 10;
    Person p2;
    p2.mA = 20;
    p2.mB = 20;
    //1.成员函数重载本质的调用 
    //Person p3 = p1.operator+(p2);//Person p3 = p1 + p2;是简化

    //2.全局函数重载本质的调用
    //Person p3 = operator+(p1, p2);//Person p3 = p1 + p2;是简化
    Person p3 = p1 + p2;
    
    //3.运算符重载 也可以发生函数重载
    Person p4 = p1 + 30;
    cout << "全局函数重载+" << endl;
    cout << "p3.mA = " << p3.mA << "\tp3.mB = " << p3.mB << endl;
    cout << "运算符重载也可以发生函数重载" << endl << endl;
    cout << "p4.mA = " << p4.mA << "\tp3.mB = " << p4.mB << endl;
}

int main() 
{
    test2();//函数重载+

    system("pause");

    return 0;
}
  1. 总结

1.对于内置数据类型的表达式的运算符是不可以改变的;

2.成员函数实现重载+,写在类内,传入一个对象;

3.全局函数实现重载+,写在类外,传入两个对象;

4.运算符重载也可以函数重载,和全局函数写法类似;

5.不要滥用运算符重载,比如operator+里边写成了两个自定义数据的相减

左移运算符重载

  1. 作用:可以输出自定义数据类型

  1. 场景描述

对于对象p在输出时,常规的左移运算符,需要加上“.m_A”或者“.m_B”才能正常输出。但是直接输出p,编译器不知道怎么输出p的属性。

int a = 10;
cout << a << endl;//编译器正常输出

Person p;
p.m_A = 10;
p.m_B = 10;
//cout << p << endl;//能否直接这样做输出?
  1. 使用全局函数,不用成员函数实现左移运算符重载的原因

  1. 不知道函数的返回值是什么的时候,先写void类型;

  1. 如果函数传入对象的话,在使用的时候还要传入一个对象,就变成p.operator<<(p),和想要的结结果不一样,所以不应该传入对象;

  1. 如果传入“cout”,就变成p.operator<<(cout),简化p << cout,这种方式可以写,但是预期效果是cout << p。使用成员函数无法达到预期效果,所以使用全局函数。

综上,不会利用成员函数重载<<运算符,因为无法实现<<在左侧。所以使用全局函数来实现。

  1. 实现过程及思路

  1. 不知道函数类型的返回值是什么的时候,先写void类型;

  1. 其次要把cout放在左边,p在右边,才有预期效果

  1. 要知道cout是什么类型,选中cout--右键 转到定义--看到cout类型是ostream。o指输出,stream指流,cout是属于标准的输出流类。此外,这个对象cout只能有一个,不能创建新对象,否则后面没法用,采用引用的方式传递。

//只能利用全局函数重载<<运算符
void operator<<(ostream& cout, Person& p)//operator<<(cout, p)  简化 cout << p
{
    cout << " mA = " << p.mA << " mB = " << p.mB;
}
  1. 注意点:如果想追加换行“endl”,会提示报错。在之前的学习中,原本的cout可以无限追加是因为使用了链式思想来编程,也就是可以一直左移。在重载左移的全局函数中,函数类型是void,没有返回值。

  1. 如果,也想用追加,那么需要函数在调用后再次返回cout,才能无限追加,因此如下修改

//只能利用全局函数重载<<运算符
ostream& operator<<(ostream& cout, Person& p)//operator<<(cout, p)  简化 cout << p
{
    cout << " mA = " << p.mA << " mB = " << p.mB;
    return cout;
}

之前使用的cout可以无限追加,其本质也是一样,调用cout输出函数的时候返回了cout。

  1. 全局函数在中形参cout也可以换成别的名字,因为这是引用的调用方式,引用的本意就是起别名,别名和原名都是指向同一块内存空间。此时把cout换成out或者其他都是可以的,并没有改变本质。例如:

//只能利用全局函数重载<<运算符
ostream& operator<<(ostream& output, Person& p)//operator<<(cout, p)  简化 cout << p
{
    output << " mA = " << p.mA << " mB = " << p.mB;
    return output;
}
  1. 当对象p的属性是私有权限内容时,比如想访问mC属性时,可以通过把重载函数做类的友元来访问。

class Person
{
    friend ostream& operator<<(ostream& cout, Person& p);
public:

    int mA;
    int mB;
private:
    int mC;
};
  1. 整体实现

#include <iostream>

using namespace std;

//不成员函数重载<<运算符原因

/*

不会成员函数重载<<运算符,因为无法实现<<在左侧。如果成员函数传入“cout”,最后效果就变成p.operator<<(cout),简化p << cout,

这种方式可以写,但是预期效果是cout << p。使用成员函数无法达到预期效果,所以使用全局函数

*/

class Person

{

friend ostream& operator<<(ostream& cout, Person& p);

public:

int mA;

int mB;

private:

int mC;

};

//只能利用全局函数重载<<运算符

ostream& operator<<(ostream& cout, Person& p)//operator<<(cout, p) 简化 cout << p

{

cout << " mA = " << p.mA << " mB = " << p.mB;

return cout;//cout只是一个别名,也可以换成其他名称output等,和原名指向同一块空间

}

void test1()

{

Person p1;

p1.mA = 56;

p1.mB = 45;

//cout << "p1.mA = " << p1.mA << endl;//编译器正常输出

cout << "p1 = " << p1 << endl;//无法输出,编译器不知道输出p1的哪个属性,需要重载<<

}

int main()

{

test1();

system("pause");

return 0;

}

  1. 总结

  1. 重载想要实现的功能,直接输出对象;

  1. 只能使用全局函数的原因;

  1. cout数据类型是输出流ostream,并且采用引用的方式传递,形参名字可以更改;

  1. 要想无限输出,重载函数应该返回cout,其类型也应是输出流ostream;

  1. 访问类的私有权限内容时,让重载函数作为类的友元

递增运算符重载

  1. 作用: 通过重载递增运算符,实现自己的整型数据

  1. 预期效果

自定义整型数据类,实现递增功能

MyInteger myint;
cout << myint << endl;//初始值是0
cout << ++myint << endl;//1,先+1再输出
cout << myint++ << endl;//1,先输出再+1
cout << myint << endl;//2
  1. 思路

  1. 代码有四个需要实现的模块

  1. 自定义整型类型数据模块

//自定义整型
class MyInteger
{
    //要让重载函数访问类的私有权限内容
    friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
    MyInteger()
    {
        m_Num = 0;
    }
private:
    int m_Num;
};
  1. 重载<<

想正常输出的话,内置cout是不认识自定义的整型类型数据的。所以需要重载<<

//重载<<
ostream& operator<<(ostream& cout, MyInteger myint)
{
    cout << "myint = " << myint.m_Num;
    return cout;
}
  1. 前置递增

//自定义整型
class MyInteger
{
    friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
    MyInteger()
    {
        m_Num = 0;
    }
    //重载前置++运算符
    MyInteger& operator++()//递增使用时直接使用++符号,因此不需要传参
    {
        m_Num++;//先进行++运算
        return *this;//再返回自身
    }
private:
    int m_Num;
};
  1. 前置递增注意点

  1. 重载递增函数应该返回的是一个整型类型的数据,即自定义的整型数据类型MyInteger,否则cout不能识别做了递增运算的变量是什么类型的数据。因此,重载递增函数的类型是自定义的整型数据类型MyInteger。

  1. 此外,重载递增函数应该返回自身,因此使用this指针。

  1. 重载递增函数使用引用的方式传递的原因:

首先,看内置前置递增是怎么实现的。上面的例子可以看出来,无论a做多少次++运算,都是对a本身进行做递增运算,并没有创建新的对象。如果不使用引用传递,采用值传递的话,最后递增重载函数返回的是创建的新对象a',而不是a本身。因此,采用引用传递,对同一块内存空间进行操作。

  1. 值传递示例

  1. 后置递增

#include <iostream>
using namespace std;

//自定义整型
class MyInteger
{
    friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
    MyInteger()
    {
        m_Num = 0;
    }
    //重载前置++运算符  返回引用
    MyInteger& operator++()//递增使用时直接使用++符号,因此不需要传参
    {
        m_Num++;//先进行++运算
        return *this;//再返回自身
    }

    //重载后置++运算符  返回值
    MyInteger operator++(int)//int代表占位参数,用于区分前置和后置递增
    {
        MyInteger temp = *this;//先记录
        m_Num++;//再进行++运算
        return temp;//最后返回结果
    }

private:
    int m_Num;
};

//重载<<
ostream& operator<<(ostream& cout, MyInteger myint)
{
    cout << "myint = " << myint.m_Num;
    return cout;
}

void test1()
{
    MyInteger myint1;
    cout << "前置递增重载" << endl;
    cout << myint1 << endl;//因为myint是自定义的MyInteger类型的整型数据,cout不知道如何输出。需要重载<<
    cout << ++myint1 << endl;

    MyInteger myint2;
    cout << "后置递增重载" << endl;
    cout << myint2 << endl;//因为myint是自定义的MyInteger类型的整型数据,cout不知道如何输出。需要重载<<
    cout << myint2++ << endl;
}

int main()
{
    test1();

    /*int a = 0;
    cout << "++a:" << ++a << endl;
    cout << "++(++a):" << ++(++a) << endl;*/

    system("pause");

    return 0;
}
  1. 后置递增注意点

  1. 占位参数区分前置递增和后置递增

在不知道返回数据类型是什么时,使用void,但此时后置递增函数和前置的发生了重定义。是因为此时两个函数的作用域相同(都在类内),函数名相同,参数也一样(都没有)。要明确重定义是同一块作用域、函数名相同,参数相同,函数类型是不能作为重定义的区分。因此在后置递增重载函数的形参传入占位参数int

  1. 先记录当前结果,再递增,最后返回结果

如果直接先返回值,那么后面的递增操作都不会再进行。因此,需要先记录当前结果,再递增。

  1. 和前置递增重载一样,后置递增重载函数类型应该是MyInteger

  1. 和前置递增重载不同的是,后置递增重载函数是值传递返回值,不能是引用传递。前置递增重载重载函数是引用传递返回引用,不能是值传递。

如果是引用传递,这个局部对象temp在第一句,红框代码执行完之后就会被释放。再返回引用就是非法操作了。如果是值传递,这个局部对象temp在第一句,红框代码执行完之后没有被释放,之后的++运算和返回都是合法的。

  1. 总结

  1. 占位参数区分前置递增重载和后置递增重载

  1. 前置递增重载返回引用,后置递增重载返回值

  1. 自定义整型数据类

赋值运算符重载

  1. c++编译器至少给一个类添加4个函数

1. 默认构造函数(无参,函数体为空)

2. 默认析构函数(无参,函数体为空)

3. 默认拷贝构造函数,对属性进行值拷贝,会引发深浅拷贝的问题,使用深拷贝解决浅拷贝。可以回顾之前的学习笔记深拷贝和浅拷贝

4. 赋值运算符 operator=, 对属性进行值拷贝,也会引发深浅拷贝的问题

  1. 深浅拷贝的问题

也就是说,只要是涉及到值拷贝,都会引发深浅拷贝的问题。如果类中有属性指向堆区,也就是有属性存放在堆区,那么就会出现堆区重复释放的问题,即浅拷贝,因此做赋值操作时也会出现深浅拷贝问题。还是要使用深拷贝来解决。

  1. 场景描述,解决浅拷贝重复释放的问题

#include <iostream>
using namespace std;

class Person
{
public:
    Person(int age)
    {
        mAge = new int(age);//age传进来,然后通过new开辟到堆区存放,并且通过mAge指针来维护
    }
    int* mAge;//把数据开辟到堆区
};

void test1()
{
    Person p1(25);
    Person p2(24);
    p2 = p1;//赋值操作
    cout << "p1的年龄是" << *p1.mAge << endl;
    cout << "p2的年龄是" << *p2.mAge << endl;
}

int main() 
{
    test1();

    system("pause");

    return 0;
}

目前为止,p1、p2都可以正常输出,并且p1的值也可以赋值给p2。但是p1、p2都是堆区的数据,堆区的数据需要程序员手动释放。需要在成员函数Person函数后添加析构函数。

    ~Person()
    {
        if (mAge != NULL)
        {
            delete mAge;
            mAge = NULL;
        }
    }

此时,程序会崩掉,出现异常断点。原因如下

  1. 存放在堆区的p1记录的是一个地址,这个地址指向的内容是18。

  1. 当写了代码,p2 = p1时,相当于把p1的数据逐字节地值拷贝给p2。

  1. 此时,p1和p2指向的都是同一块内存空间。当写了析构的代码之后,p2执行完之后,析构判断一下p2是否为空指针,此时不是空指针,需要释放。

  1. 接着p1也需要经过相同的过程进行释放。但是之前这块内存已经被释放了,再次释放就导致程序异常,也就是出现了浅拷贝的问题,重复释放。

  1. 因此,需要用深拷贝来解决这个问题。也就是重新开辟一块内存,让p2指向这块内存,并且内存存放的内容和p1指向的内存内容一致。

  1. 要实现上面的功能,就不能再用内置的赋值运算符了,需要重载赋值运算符。

  1. 整体实现

#include <iostream>
using namespace std;

class Person
{
public:
    Person(int age)
    {
        mAge = new int(age);//age传进来,然后通过new开辟到堆区存放,并且通过mAge指针来维护
    }
    ~Person()
    {
        if (mAge != NULL)
        {
            delete mAge;
            mAge = NULL;
        }
    }
    //重载
    Person& operator=(Person& p)
    {
        //编译器实现浅拷贝的本质:mAge = p.mAge;  不能做简单的浅拷贝
        //如果有属性在堆区,首先应该释放干净,即p2已经是堆区的数据,首先需要把p2释放干净
        if (mAge != NULL)
        {
            delete mAge;
            mAge = NULL;
        }
        //深拷贝:new开辟一块新的内存空间,这个空间大小就是p->mAge的大小,
        //所以通过解引用获得内存大小,再通过mAge来接收
        mAge = new int(*p.mAge);
        //返回自身
        return *this;
    }
    int* mAge;//把数据开辟到堆区
};

void test1()
{
    Person p1(25);
    Person p2(24);
    Person p3(30);
    //p2 = p1;//赋值操作
    p3 = p2 = p1;
    cout << "p1的年龄是" << *p1.mAge << endl;
    cout << "p2的年龄是" << *p2.mAge << endl;
    cout << "p3的年龄是" << *p3.mAge << endl;
}

int main() 
{
    test1();
    //int a = 10;
    //int b = 20;
    //int c = 30;

    //c = b = a;
    //cout << "a = " << a << endl;//10
    //cout << "b = " << b << endl;//10
    //cout << "c = " << c << endl;//10

    system("pause");

    return 0;
}

注意点

  1. 编译器实现浅拷贝的本质是“mAge = p.mAge; ”,为了避免浅拷贝,不能做简单的值拷贝;

  1. 如果有属性在堆区,首先应该释放干净。即p2已经是堆区的数据,首先需要把p2释放干净。

  1. 内置赋值运算符是一个可以连等的。所以在赋值运算符重载函数里边,为了能够实现连等操作,也就是能够让p2 = p1执行完后返回自身,这时才能实现连等操作。因此赋值运算符重载函数的类型应该是Person,并且使用引用的方式。不能返回值,前面也有类似的例子解释过了。

  1. 总结

  1. 为什么要重载赋值运算符?编译器提供的赋值运算符是一个浅拷贝的操作,当有些成员属性创建在堆区,比如指针。这时就不能使用内置赋值运算符了,需要重载赋值运算符,否则会带来浅拷贝的重复释放的问题。

  1. 重载时,首先要判断堆区指针有没有释放干净。

  1. 如果释放干净了,再做深拷贝,新开辟一块内存,存放相同的内容。

  1. 为了实现连等的操作,使用重载函数使用引用的方式传递,返回自身。

关系运算符重载

  1. 作用:重载关系运算符,可以让两个自定义类型对象进行对比操作

  1. 场景描述

对于内置的数据类型,编译器知道进行关系比较(相等和不等),例如:

int a = 25;
    int b = 24;
    if (a == b)
    {
        cout << "a和b是相等的" << endl;
    }

但对于自定义的数据类型,编译器就不知道进行关系比较了,如下,此时需要进行关系运算符的重载。

    Person p1;
    Person p2;
    if (p1 == p2)
    {
        cout << "p1和p2是相等的" << endl;
    }
  1. 整体实现

#include <iostream>
using namespace std;

class Person
{
    friend ostream& operator<<(ostream& cout, Person& p);//全局函数做友元
public:
    Person(string name, int age)
    {
        mName = name;
        mAge = age;
    }
    //重载==号
    bool operator==(Person& p)
    {
        if (this->mName == p.mName && this->mAge == p.mAge)
        {
            return true;
        }
        return false;
    }
    //重载!=号
    bool operator!=(Person& p)
    {
        if (this->mName == p.mName && this->mAge == p.mAge)
        {
            return false;
        }
        return true;
    }
private:
    string mName;
    int mAge;
};

//重载<<  注意 引用传递 ostream数据流类型 
ostream& operator<<(ostream& cout, Person& p)
{
    cout << "姓名:" << p.mName;
    cout << "年龄:" << p.mAge;
    return cout;
}
void test1()
{
    Person p1("Judge", 24);
    Person p2("Bella", 25);
    Person p3("Bella", 25);
    cout << p1 << endl;
    cout << p2 << endl;
    cout << p3 << endl << endl;

    if (p2 == p3)
    {
        cout << "等号重载函数:\tp3和p3是相等的" << endl << endl;
    }
    else
    {
        cout << "等号重载函数:\tp2和p3是不相等的" << endl << endl;
    }
    if (p2 != p1)
    {
        cout << "不等号重载函数:p2和p1是相等的" << endl << endl;
    }
    else
    {
        cout << "不等号重载函数:p2和p1是不相等的" << endl << endl;
    }
}

int main()
{
    test1();
    

    system("pause");

    return 0;
}

效果

  1. 注意点

  1. 在判断相等和不等时,相等就是真,返回true,不相等就是假,返回false,所以重载函数的类型是布尔类型bool;

  1. 为了便于一次性输出类对象,我加了左移重载函数。重载左移时,只能用全局函数来实现以及使用引用传递,并且这个函数要在类中做友元,还要注意左移符号的数据类型。

  1. 总结

  1. 对比自定义数据类型时,需要重载==和!=。

  1. 重载函数是bool类型,因为判断==和!=,只需要知道结果是真还是假,也就是返回值是true还是false。

函数调用运算符重载

  1. 函数调用运算符

  • 函数调用运算符: (),这个符号也可以重载。

  • 仿函数:由于重载后的函数调用运算符(),其使用的方式非常像函数的调用,因此称为仿函数

  • 仿函数没有固定写法,非常灵活。在STL中使用比较多。

  1. 仿函数——例1:打印输出

#include <iostream>
using namespace std;

class MyPrint
{
public:
    //重载函数调用运算符()
    void operator()(string test)
    {
        cout << test << endl;
    }
};

void MyPrint2(string test)
{
    cout << test << endl;
}

void test1()
{
    MyPrint myprint;
    cout << "重载函数调用" << endl;
    myprint("Bella is studying.");//由于使用方式与函数调用非常类似,称其为仿函数

    cout << "\n函数调用" << endl;
    MyPrint2("Bella is studying."); 
}



int main()
{
    test1();

    system("pause");

    return 0;
}

MyPrint myprint()仿函数中,把实参传给形参test,再进行打印输出。

  1. 仿函数没有固定写法——例2:加法类

class MyAdd
{
public:
    int operator()(int num1, int num2)
    {
        int num = num1 + num2;
        return num;
    }
};

void test2()
{
    int a = 20;
    int b = 10;
    MyAdd myadd;
    cout << "a = " << a << "\tb = " << b << "\na + b = " << myadd(a, b) << endl;

}

可以看到第一个例子的返回类型是void,第二个例子的返回类型是int,仿函数没有固定写法,仿函数依据程序员需求来编写。

  1. 匿名函数对象

MyAdd()就是匿名函数对象,通过一个类型+(),编译器会创建一个匿名对象。匿名对象的特点就是在当前行执行完之后,就会立即被释放。因为实现100+100之后,我们不需要这个对象了,所以可以创建匿名对象。看到匿名函数对象,就应该想到这是给匿名对象,并且这个函数重载了()。

  1. 总结

  1. 仿函数,重载();

  1. 仿函数没有固定写法;

  1. 仿函数使用方法,可以创建变量名接收仿函数的返回值;也可以不创建变量名接收仿函数的返回值,即创建匿名对象;

  1. 匿名函数对象,形式:类型+();特点:当前行执行完之后立即被释放。

终于学完运算符重载了~~~

黑马老师

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值