第四集.C++学习

1.运算符重载

除了函数,运算符也可以重载。运算符重载的方法时定义一个重载运算符的函数,在需要执行被重载的运算符时,系统自动调用该函数,以实现运算。(运算符的本质也是函数的重载)

重载运算符的格式一般如下:

函数类型 operator 运算符名称 (形参列表)
{
    重载处理
}

 举一个不恰当的例子,比如把+变成减的作用:

int operator + (int a, int b)
{
    return (a-b);
}

我们通过例子来讲述如何重载运算符(类方法中)

法一:通过类方法进行

#include <iostream>
using namespace std;
/*定义只因类*/
 class Complex
 {
 public:
    //构造器
    Complex();
    //重载函数,输入参数不同 ,功能相同
    Complex(double r, double i);
 
    //析构器
    ~Complex();
 
    //方法
    //加入一个方法,实现复数加法,Complex指其类型
    Complex Complex_add(Complex &d);
    void print();//打印信息
 
 private:
    double real;
    double imag;
 };
//构造函数实现
Complex::Complex()
{
    real = 0;
    imag = 0;
}
//重载的构造函数实现
Complex::Complex(double r,double i)
{
    real = r;
    imag = i;
}
//析构器的实现呢
Complex::~Complex()
{
 
}
//基类的方法实现
//Complex &d为定义基类对象d(被加数) ;complex_add为基类Complex方法
//第一个Complex为其类型;第二个Complex为继承基类的加数对象
Complex Complex::Complex_add(Complex &d)
{
    Complex c;//定义基类对象
 
    c.real = d.real + real;
    c.imag = d.imag + imag;
 
    return c;
}
void Complex::print()
{
    cout << "(" << real << "+" << imag << "i)" << endl;
}
 
 int main()
 {
    Complex c1(3,4),c2(5,6),c3;
    c3 = c1.Complex_add(c2);
 
    cout << "c1 = ";
    c1.print();
    cout << "c2 = ";
    c2.print();
    cout << "c1 + c2 = ";
    c3.print();
 
 
     return 0;
 }

如上可见采用类方法进行修改非常的麻烦,这个时候我们就可以采用重载运算符的方法。分为两个方法,首先是方法2_1:

/*法二采用重载运算符*/
 #include<iostream>
 using namespace std;
 
 //定义基类
 class Complex
 {
 public:
    //构造器
    Complex();
    //重载函数,输入参数不同 ,功能相同
    Complex(double r, double i);
 
    //析构器
    ~Complex();
 
    //方法
    void print();//打印信息
    //友元关系,Complex指其类型
    friend Complex operator + (Complex &c,Complex &d);/*作为非成员函数*/
 
 private:
    double real;
    double imag;
 };
//构造函数实现
Complex::Complex()
{
    real = 0;
    imag = 0;
}
//重载的构造函数实现
Complex::Complex(double r,double i)
{
    real = r;
    imag = i;
}
//析构器的实现呢
Complex::~Complex()
{
 
}
/*运算符重载函数除了可以作为类的成员函数之外,还可以是非成员函数,放在类外,但是必须是友元函数*/
//基类的方法实现,没有用到类本身的实例,所以需要友元函数
Complex operator + (Complex &c,Complex &d)/*直接是地址,那c和d就是解引用,所以用.   而不是用->*/
{
    return Complex(c.real + d.real, c.imag + d.imag);/*返回函数Complex(double r,double i),记得!*/
    /*看这个返回值是一个构造器,构造了一个类的实例相当于*/

}
/*另一种写法,这种写法不需要友元函数,因为用的就是俩参数,把friend去掉
Complex Complex::operator + (Complex &d)   不用想为啥就一个参数,加号本身就是俩参数,所以重载了肯定也是两个,这里只是一个参数是类实例本身一个是
{
    Complex c;
    c.real = real + d.real;  第一个real是第一个参数的。第二个d.real是第二个参数的!
    c.imag = imag + d.imag;

    return c;
}


*/
 
void Complex::print()
{
    cout << "(" << real << "+" << imag << "i)" << endl;
}
 
 int main()
 {
    Complex c1(3,4),c2(5,6),c3;/*前两个通过函数Complex(double r,double i)进行初始化*/
    c3 = c1 + c2;/*operator + 就是被重载的加号,相当于一个函数,c1和c2就是调用的参数*/
 /*c1+c2的返回值是一个类的实例,所以可以调用*/
    cout << "c1 = ";
    c1.print();
    cout << "c2 = ";
    c2.print();
    cout << "c1 + c2 = ";
    c3.print();
 
 
     return 0;
 }


法2_2:未采用友元函数,因为重载操作符的函数中只有一个参数,那是因为本身的类的实例就是第一个参数,而函数中后来给的参数是第二个参数 ,而针对于友元函数,本身类的实例是不算的,因为可以是两个参数(+号原本就是为两个参数服务的),记得写法,对于友元函数,相当于类外的函数,不是类的成员函数,因为并不需要Complex::进行调用,但未加friend那就必须要加Complex::进行调用了。

/*法二采用重载运算符*/
 #include<iostream>
 using namespace std;
 
 //定义基类
 class Complex
 {
 public:
    //构造器
    Complex();
    //重载函数,输入参数不同 ,功能相同
    Complex(double r, double i);
 
    //析构器
    ~Complex();
 
    //方法
    void print();//打印信息
    //友元关系,Complex指其类型
    Complex operator + (Complex &c,Complex &d);/*作为非成员函数*/
 
 private:
    double real;
    double imag;
 };
//构造函数实现
Complex::Complex()
{
    real = 0;
    imag = 0;
}
//重载的构造函数实现
Complex::Complex(double r,double i)
{
    real = r;
    imag = i;
}
//析构器的实现呢
Complex::~Complex()
{
 
}

/*另一种写法,这种写法不需要友元函数,因为用的就是俩参数,把friend去掉*/
Complex Complex::operator + (Complex &d)   不用想为啥就一个参数,加号本身就是俩参数,所以重载了肯定也是两个,这里只是一个参数是类实例本身一个是
{
    Complex c;
    c.real = real + d.real;  第一个real是第一个参数的。第二个d.real是第二个参数的!
    c.imag = imag + d.imag;

    return c;
}


 
void Complex::print()
{
    cout << "(" << real << "+" << imag << "i)" << endl;
}
 
 int main()
 {
    Complex c1(3,4),c2(5,6),c3;/*前两个通过函数Complex(double r,double i)进行初始化*/
    c3 = c1 + c2;/*operator + 就是被重载的加号,相当于一个函数,c1和c2就是调用的参数*/
 /*c1+c2的返回值是一个类的实例,所以可以调用*/
    cout << "c1 = ";
    c1.print();
    cout << "c2 = ";
    c2.print();
    cout << "c1 + c2 = ";
    c3.print();
 
 
     return 0;
 }


最后我们可以得到以下规矩:

  1.  重载不能改变运算符运算对象(操作数)个数
  2.  重载不能改变运算符的优先级别
  3.  重载不能改变运算符的结合性
  4.  重载运算符的函数不能有默认函数
  5.  重载运算符必须和用户定义的自定义类型的对象一起使用器参数至少有一个是类对象(为了防止用户修改标准类型的运算符性质)

 在类中定义的重载运算符,因为必须是类实例出来的对象,在应用这个运算符的时候才会有作用,其他使用+,还是普通的加法。记得返回类型也应该是类的类型,这样才能用里面的函数

 重载仅仅是用于C++没有的东西,不要乱创意!
 而且只对类的实例有用,不对类实例进行使用,就还是原本的普通运算符

作业:运算符重载实现有理数(分式)的加减乘除

 #include<iostream>
 using namespace std;

 #include<string>
 #include<cmath>
 
 //定义基类
 class Rational
 {
 public:
    //构造器
    Rational(int num1, int den1);
 
    //析构器
    ~Rational();
 
    //方法
    Rational operator + (Rational rhs);//rhs = right hand side(右边的参数)
    Rational operator - (Rational ths);
    Rational operator * (Rational ths);
    Rational operator / (Rational ths);
    void print();//打印信息
 private:
    void normalize();//负责对分数的简化处理
    int num;//分子
    int den;//分母
 };
//构造函数实现
Rational::Rational(int num1, int den1)
{
    num = num1;
    den = den1;
 
    normalize();//对分数化为最简形式
}
//析构器的实现呢
Rational::~Rational()
{
 
}
//基类的方法实现
void Rational::normalize()
{
//normalize()对分数进行简化操作包括:
//1.只允许分子为负数,如果分母为负数则把负数挪到分子部分,如1/-2==-1/2
//2.利用欧几里德算法(辗转求余原理)将分数进行简化:2/10 => 1/5
    if(den < 0)
    {
        den = -den;
        num = -num;
    }
    //辗转求余原理:求解分子和分母的最大公约数
    int a = abs(num);
    int b = abs(den);
    int bc,cs,ys;//定义被除数,除数,余数
    if (a > b)
    {
        bc = a;
        cs = b;
    }
    else
    {
        bc = b;
        cs = a;
    }
    ys = bc % cs;
    while(ys > 0)
    {
        bc = cs;
        cs = ys;
        ys = bc % cs;
    }
    //分子,分母同时除以最大公约数
    num = num / cs;
    den = den / cs;
}
//分式加法运算
//a   c   a*d   c*b   a*d + c*d
//- + - = --- + --- = ----------
//b   d   b*d   b*d      b*d
Rational Rational::operator + (Rational rhs)
{
    int e,f;
 
    e = num * rhs.den + rhs.num * den;
    f = den * rhs.den;
    return Rational(e,f);
}
//分式减法运算【转化为加法运算】
//a   c   a   -c
//- - - = - + --
//b   d   b   d
Rational Rational::operator - (Rational rhs)
{
    rhs.num = -rhs.num;
    return operator + (rhs);
}
//分式乘法运算
//a   c   a*c
//- * - = ---
//b   d   b*d
Rational Rational::operator*(Rational rhs)
{
    int e,f;
    e = num * rhs.num;
    f = den * rhs.den;
    return Rational(e,f);
}
//分式除法运算
//a   c   a   d
//- / - = - * -
//b   d   b   c
Rational Rational::operator/(Rational rhs)
{
    int e,f;
    e = num * rhs.den;
    f = den * rhs.num;
    return Rational(e,f);
}
 
void Rational::print()
{
    cout << num << "/" << den << endl;
}
 
 int main()
 {
    Rational c1(3,4);
    Rational c2(2,4);
 
//有理数加法验证
    Rational c3 = c1 + c2;//这里不可以为Rational c3; c3 =c1 + c2;[因为Rational类型为有参数的]
    cout << "c1 = ";
    c1.print();
    cout << "c2 = ";
    c2.print();
    cout << "c1 + c2 = ";
    c3.print();
//有理数减法验证
    cout << "\n\n";
    Rational c4 = c1 - c2;
    cout << "c1 = ";
    c1.print();
    cout << "c2 = ";
    c2.print();
    cout << "c1 - c2 = ";
    c4.print();
//有理数乘法验证
    cout << "\n\n";
    Rational c5 = c1 * c2;
    cout << "c1 = ";
    c1.print();
    cout << "c2 = ";
    c2.print();
    cout << "c1 * c2 = ";
    c5.print();
//有理数除法验证
    cout << "\n\n";
    Rational c6 = c1 / c2;
    cout << "c1 = ";
    c1.print();
    cout << "c2 = ";
    c2.print();
    cout << "c1 / c2 = ";
    c6.print();
 
    return 0;
 }

 /*重载仅仅是用于C++没有的东西,不要乱创意
 而且只对类的实例有用,不对类实例进行使用,就还是原本的普通运算符*/

2.多继承

什么是多继承,什么时候用到多继承,我们可以举一个例子:

在学校里有老师和学生,他们都是人(person),我们可以用"老师是人"和"学生是人"来描述,根据对象描述我们应该定义一个基类是人,然后两个子类是老师和学生,但有一部分学生当助教,这样他又是老师又是助教,所以需要写一个类同时继承老师类和学生类,需要用多继承。(就相当于一个人继承了多个人的特质。

#include <iostream>
using namespace std;
#include <string>
 
class Person
{
public:
    //构造器
    Person(string theName);
    //基类的方法
    void introduce();
 
protected:
    string name;
};
//基类构造器实现
Person::Person(string theName)
{
    name = theName;
}
//基类方法实现
void Person::introduce()
{
    cout << "我是一位" << name << endl;
}
class Teacher : public Person
{
public:
    //子类构造器
    Teacher(string theName, string theClass);//老师的名字,在哪个班级教学
    //子类的方法
    void teach();//教书
    //该函数不是重载,不是覆盖,而是子类隐藏了基类方法
    void introduce();//自我介绍
 
protected:
    string classes;
};
//子类构造器实现
Teacher::Teacher(string theName, string theClass) : Person(theName)//老师的名字继承于人类中的名字
{
    classes = theClass;
}
//子类方法实现
void Teacher::teach()
{
    cout << "我教" << classes << endl;
}
void Teacher::introduce()//该函数不是重载,不是覆盖,而是子类隐藏了基类方法
{
    cout << "大家好,我是" << name << ",我教" << classes << endl;
}

class Student : public Person
{
public:
    //构造器
    Student(string theName, string theClass);//学生姓名,上什么课程
    //方法
    void attent();//上课
    void introduce();//自我介绍
protected:
    string classes;
};
//子类2构造器实现
Student::Student(string theName, string theClass):Person(theName)
{
    //因为name已经继承基类,所以此时无需再幅值。
    classes = theClass;
}
//子类方法实现
void Student::attent()
{
    cout << name << "加入" << classes << "学习" << endl;
}
void Student::introduce()
{
    cout << "我是" << name << ",我在" << classes << "学习" << endl;
}

//多继承,既继承老师,也继承学生
class Assistant:public Teacher,public Student//!!注意这里都需要写public
{
public:
    //构造器
    Assistant(string theName, string classTeach, string classAttend);
    //方法
    void introduce();
};
//助教:构造器实现
Assistant::Assistant(string theName, string classTeach, string classAttend):
    Teacher(theName,classTeach),Student(theName,classAttend)
{
    ;
}
void Assistant::introduce()
{
    //这里注意Student::name不是Student.name!!!
    cout << "大家好,我是" << Student::name << "上课在" << Student::classes;
    cout << "同时教学在" << Teacher::classes << "\n";
}
 
 
int main()
{
    Teacher teacher("小甲鱼", "C++入门");
    Student student("迷途羔羊", "C++入门");
    Assistant assistent("丁丁", "C++入门", "C++进阶");
    //老师
    teacher.introduce();
    teacher.teach();
    //学生
    cout << "\n";
    student.introduce();
    student.attent();
    //助教
    cout << "\n";
    assistent.introduce();
    assistent.teach();
    assistent.attent();
 
    return 0;
}

我们可以注意这段代码,首先是继承这俩子类的,所以两个子类都需要写public,然后进行构造器实现。

class Assistant:public Teacher,public Student//!!注意这里都需要写public
{
public:
    //构造器
    Assistant(string theName, string classTeach, string classAttend);
    //方法
    void introduce();
};
//助教:构造器实现
Assistant::Assistant(string theName, string classTeach, string classAttend):
    Teacher(theName, classTeach),Student(theName, classAttend)
{
    ;
}

教师和学生的名字(theName)应该是同一个,不然会造成出现两个人的情况,助教的name1和name2分别继承了学生和老师的,会导致我们原本只想派生一个人,现在反而变成两个人了比如:

class Assistant:public Teacher,public Student//!!注意这里都需要写public
{
public:
    //构造器
    Assistant(string theName, string classTeach, string classAttend);
    //方法
    void introduce();
};
//助教:构造器实现
Assistant::Assistant(string theName1, string theName2, string classTeach, string classAttend):
    Teacher(theName1,classTeach),Student(theName2,classAttend)
{
    ;
}

想要解决这个问题。就必须要用到虚继承。

3.虚继承

在进行多继承的时候,一个类继承了多个类的属性,可以能会导致麻烦(比如一个人有了俩名字,一个人有了俩鼻子)这有可能引发麻烦,为此提供了虚继承来解决。

通过虚继承某类,告诉编译器,从当前这个类再派生出来的子类就只能拥有那个基类的一个实例:

虚继承语法:
class Teacher:virtual public Person

 那么我们让Student和Teacher类都虚继承自Person类,这样这俩者派生的子类只能拥有一份Person的属性,且虚继承后,虚继承的类无法拷贝基类的属性和方法(这个意思是子类派生的多继承的类,公共值如名字,只能从基类进行赋值)

如下:

#include <iostream>
using namespace std;
#include <string>
 
class Person
{
public:
    //构造器
    Person(string theName);//theName为类的输入参数
    //基类的方法
    void introduce();
 
protected:
    string name;
};
//基类构造器实现
Person::Person(string theName)
{
    name = theName;
}
//基类方法实现
void Person::introduce()
{
    cout << "我是一位" << name << endl;
}

//虚继承,再有类继承Teacher类时,将只拥有一份Person类的属性
class Teacher : virtual public Person
{
public:
    //子类构造器
    Teacher(string theName, string theClass);//老师的名字,在哪个班级教学
    //子类的方法
    void teach();//教书
    //该函数不是重载,不是覆盖,而是子类隐藏了基类方法
    void introduce();//自我介绍
 
protected:
    string classes;
};
//子类构造器实现
Teacher::Teacher(string theName, string theClass) : Person(theName)//老师的名字继承于人类中的名字
{
    classes = theClass;
}
//子类方法实现
void Teacher::teach()
{
    cout << "我教" << classes << endl;
}
void Teacher::introduce()//该函数不是重载,不是覆盖,而是子类隐藏了基类方法
{
    cout << "大家好,我是" << name << ",我教" << classes << endl;
}

//虚继承,再有类继承Student类时,将只拥有一份Person类的属性
class Student : virtual public Person
{
public:
    //构造器
    Student(string theName, string theClass);//学生姓名,上什么课程
    //方法
    void attent();//上课
    void introduce();//自我介绍
protected:
    string classes;
};
//子类2构造器实现
Student::Student(string theName, string theClass):Person(theName)
{
    //因为name已经继承基类,所以此时无需再幅值。
    classes = theClass;
}
//子类方法实现
void Student::attent()
{
    cout << name << "加入" << classes << "学习" << endl;
}
void Student::introduce()
{
    cout << "我是" << name << ",我在" << classes << "学习" << endl;
}

//多继承,既继承老师,也继承学生
class Assistant:public Teacher,public Student//!!注意这里都需要写public
{
public:
    //构造器
    Assistant(string theName, string classTeach, string classAttend);
    //方法
    void introduce();
};
//助教:构造器实现
//由于虚继承,Teacher和Student都不能拥有Person类中的属性和方法
//只能由Person类自己给出
Assistant::Assistant(string theName, string classTeach, string classAttend):
                    Teacher(theName,classTeach),
                    Student(theName,classAttend),
                    Person(theName)/*这里需要加一个Person类进行继承,得到它的name属性,因为虚继承,老师和学生无法继承人的属性和方法,所以还是向人进行继承*/
{
    //多继承 助教既继承老师类,又继承学生类
}
void Assistant::introduce()
{
    //这里注意Student::name不是Student.name
    cout << "大家好,我是" << Student::name << "上课在" << Student::classes;
    cout << "同时教学在" << Teacher::classes << "\n";
}
 
 
int main()
{
    Teacher teacher("小甲鱼", "C++入门");
    Student student("迷途羔羊", "C++入门");
    Assistant assistent("丁丁", "C++入门", "C++进阶");
    //老师
    teacher.introduce();
    teacher.teach();
    //学生
    cout << "\n";
    student.introduce();
    student.attent();
    //助教
    cout << "\n";
    assistent.introduce();
    assistent.teach();
    assistent.attent();
 
    return 0;
}

注意这个地方进行构造的时候,多了一个person(theName),名字是从person的构造器进行构造的,只能有一个,因此不会出现定义出两个人的问题 

Assistant::Assistant(string theName, string classTeach, string classAttend):
                    Teacher(theName,classTeach),
                    Student(theName,classAttend),
                    Person(theName)

4.动态内存管理!!

很好的参考资料:看完这篇你还能不懂C语言/C++内存管理? - 知乎 (zhihu.com)

很多时候,需要存储的数据量到底有多大在事先往往是一个未知数,要想处理好这类情况,就需要在C++程序里使用动态内存。

动态内存由一些没有名字,只有地址的内存块构成,那些内存块是在程序运行期间进行动态分配的,它们来自C++标准库替你管理的大池子"内存池",从内存池申请内存需要用new语句,它将根据你提供的数据类型分配一块大小适当的内存。如果友足够的可用内存满足你的申请,new语句将返回新分配地址块的起始地址。如果没有足够的可用内存空间,就会抛出bad_alloc的异常,用完内存块必须用delete进行释放,且把关联的指针设置为NULL!

  • 指针变量名 = new 类型标识符;
  • 指针变量名 = new 类型标识符(初始值);
  • 指针变量名 = new 类型标识符[内存单元个数];

每一个new必须对应一个delete

int *i = new int;
delete i;
i = NULL;//直接指向空(无法通过一个被设置为NULL的指针去访问数据)

new语句返回的内存很可能充满垃圾数据,所以我们往往先朝里面写东西覆盖,再进行访问,或者直接在类直接写一个构造器进行初始化。

代码如下:

#include <iostream>
using namespace std;
#include <string>
 

class Company
{
public:
    //构造器
    Company(string theName);
    //方法
    virtual void printInfor();//打印信息
 
protected:
    string name;
};
//基类构造器实现
Company::Company(string theName)
{
    name = theName;
}
//基类方法实现
void Company::printInfor()
{
    cout << "这家公司的名字叫:" << name << endl;
}

class TechCompany:public Company
{
public:
    //构造器
    TechCompany(string theName, string product);
    //方法
    void printInfor();//打印信息
private:
    string product;
};
//构造器实现
TechCompany::TechCompany(string theName, string product):
            Company(theName)/*theName是从基类进行的*/
{
    this->product = product;/*this指向的product就是private里的那个,右边是参数*/
}
//方法实现
void TechCompany::printInfor()
{
    cout << name << "公司生产了" << product << "\n\n";
}
 
 
int main()
{
    /*new相当于malloc升级,(a*)malloc(sizeof(a))就等于new a*/
    Company *company = new Company("苹果");/*分配地址(内存)*/
    company->printInfor();
 
    delete company;//删除指针地址
    company = NULL;//为指针指向的地址赋空值
    /*重新分配*/
    company = new TechCompany("APPLE", "IPHONE");/*删除了地址,但其实company还存在,只不过指向NULL*/
    company->printInfor();
    delete company;//删除指针地址
 
    return 0;
}

/*有一点要注意,如果直接是int &a,那么a不当成指针哦!用a. ,如果是int *a,则用a->*/
class A
   {
    private:
      char *m_cBuffer;
      int m_nLen;

   `` public:
      A(){ m_cBuffer = new char[m_nLen]; }
      ~A() { delete [] m_cBuffer; }
   };

   A *a = new A[10];
   delete a;         //仅释放了a指针指向的全部内存空间 但是只调用了a[0]对象的析构函数 剩下的从a[1]到a[9]这9个用户自行分配的m_cBuffer对应内存空间将不能释放 从而造成内存泄漏
   delete[] a;      //调用使用类对象的析构函数释放用户自己分配内存空间并且   释放了a指针指向的全部内存空间

5.动态数组

虽然前面讲过的用new给基本类型和对象在运行时分配内存,但它们的尺寸在编译时就已经确定下来,因为我们为之申请内存的数据类型在程序中有明确的定义,有明确的单位长度;但有些时候,必须等到程序运行时才能确定需要申请多少内存,甚至还需要根据程序的运行情况追加申请更多的内存。

注:指针和数组

int a[20];
int *x = a;函数名是指针!或者是第一个元素的地址即&a[0]也是指针,int *x = &a[0]
指针变量x指向数组a的地址,a[0]和*x都代表数组的第一个元素
a[1]等价于*(x + 1)

 建立动态数组:

int *x = new int[10];   /*x表示整型数组的数组名*/
/*可以像对待一个数组那样使用指针变量x*/
x[1] = 45;
x[2] = 8;

当然也可以用一个变量来保存该数组的元素个数:

int count = 10;
int *x = new int[count];

删除一个动态数组:

delete[]x;

案例代码如下:

#include <iostream>
#include <string>
using namespace std;
 
int main()
{
    unsigned int count = 0;
 
    cout << "请输入数组的元素个数:" << '\n';
    cin >> count;
    //程序运行时才申请,不是在编译的时候,所以叫做动态的
    //在堆里面申请,不是在栈【一般局部变量在栈中申请】里申请 。
    int *x = new int[count];
    //数组赋值
    for(int i = 0; i< count; i++)
    {
        x[i] = i;
    }
    //输出打印
    for(int i = 0; i< count; i++)
    {
        cout << "x[" << i << "] = " << x[i]  << endl;
    }
 
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值