类的三大特性

一、封装:
方法:
1、使用类将数据成员和函数方法绑定在一起。
2、使用访问限制符进行限制封装。

作用:信息隐藏的作用,只提供一些必要的接口,而隐藏那些不必要的接口。避免受到外界的干扰和误用,从而确保安全
破坏封装:友元类可访问该类中的所有数据和函数。破化了类的封装性

二、继承
继承:
1、作用:通过子类(派生类)继承父类(基类)的成员,共享不同的东西,实现各自不同的东西,达到代码复用的作用。
继承是类型之间的建模
子类是父类的的一部分,子类可继承父类中的非static成员。
3、继承方式:
访问修饰符:
public——公有(所有权限)
protected—保护(仅在类中可访问,子类继承父类的public成员后,为protected属性,子类可访问)
private—–私有(仅在类中可访问,类外及子类中不可见(继承但不可访问))
成员属性和继承方式最终取权限小的:
即:
成员属性 继承方式 子类继承后的属性
public public–protected—private public–protected—private
protected public–protected—private protected–protected—private
private public–protected—private private—private—private
子类和父类属于不同的作用域,可出现同名的成员,但会构成重定义(隐藏),隐藏父类的成员,屏蔽对父类该成员的直接访问。若要调用父类的成员,则需加类名指定
注:在实际使用中最好不要定义同名的成员,防止出错。
public继承是一种is–a的关系,即子类是父类的一部分,子类可继承使用父类中所有的public成员。
4、赋值兼容规则:
可直接将子类对象赋给父类,即切片(将父类的部分切分给父类),此方法不是一种类型转换。可使用引用的方法接收验证
父类对象不可赋给子类,但可通过强转将父类的指针或引用赋给子类,但使用时还是会出现错误
5、构造函数问题:
注:子类由复合构成,初始化子类中父类的数据成员时,必须显示的调用父类的构造函数,
且当父类无缺省的带参构造函数时,必须在子类构造函的初始化列表中显示调用父类的构造函数,进行初始化父类的成员。
因为:调用子类的构造函数时,会在子类的初始化列表处,调用父类的构造函数,
若此处不显示调用父类的构造函数,则编译器也会寻找父类中无参的构造函数,但父类中已定义带参的构造函数,所以会因找不到匹配的构造函数出错。
缺省的带参函数 = 无参函数+带参函数。
6、拷贝构造问题:
子类初始化父类时,可直接将子类对象传给父类,父类对象会进行切片,将属于父类的部分切分下来进行初始化父类。
7、赋值运算符重载:
注:由于父类的赋值运算符重载函数,与子类的该函数属于同名函数,所以两个函数构成了重定义,子类的该函数隐藏了父类的函数。从而会默认调用子类的赋值函数(就近原则)。
所以在调用父类的赋值函数时,必须加上类域指定调用父类的赋值函数,否则会默认调用子类的赋值函数,从而构成递归。
8、析构函数:
子类和父类的析构函数名虽然表面上看到的相同,但在底层上实际被处理成同名的函数,两者构成了重定义。
所以显示调用父类的析构函数时,必须加上类域指定为父类的,否则会默认的调用子类的析构函数。
调用析构函数时,会先调用子类析构函数,后自动调用父类析构函数。
因为:构造函数时,先调用的是父类构造函数,后构造子类部分,根据栈的先进后出特性,所以不可先析构父类。
protected/private继承是一种has-a关系,类外不可访问,
对于私有继承,子类内不可访问父类成员,
保护继承,子类内可访问父类非私有成员。
列:
AA a;//父类对象
BB b; //子类对象
a = b; //子类对象赋给父类
不是类型转化:
b = a; //父类对象赋给子类,错误
AA & aa = b //可引用,说明未发生类型转换
9、如何定义一个不可继承的类?
解1:将类的构造函数定义成私有的。
原因:1、类的私有成员,外部不可见,子类也不可见。
2、子类是合成的,调用子类的构造函数时,会先调用父类的构造函数,但父类的构造函数不可见,所以无法实例化处对象。所以该类不可继承。
注:若类外要实例化出父类对象,则还需在父类中定义一个static接口函数,类外通过类域指定调用。才能实例化出对象。由于static成员是不继承的,所以子类还是无法调用父类的构造。
解2:将类定义成抽象类,抽象类无法实例化出对象。
分类:
1、单继承:子类只有一个直接父类的继承
2、多继承:子类有多个直接父类的继承方式
3、菱形继承:单继承和多继承的复合。
列:菱形继承

   A
  / \
 B   C
  \ /
   D

A类中的数据被B,C类继承,B,C再被D继承,从而导致D类中有多个A中的数据成员,从而导致数据二义性和数据沉于问题。
虚继承:
方法:通过在继承方式修饰符前加virtual 关键字进行修饰,即为虚继承。
1、虚继承是为了解决菱形继承产生的二义性和数据沉于问题。
非虚继承时:同名的成员的数据成员相互独立,数值不同,使用时必须指定是哪个父类,否则会造成二义性问题,运行不通过,使用类与指定后可解决二义性问题,但无法处理数据沉于问题(类中有多个该数据成员)。
虚继承:对同名的数据成员操作,实际是对他们公共父类中的该数据成员操作,两父类的该数据成员值相同。从而可以解决数据二义性问题和数据沉于问题,类中有1个该数据成员
2、虚继承会产生虚基表,用于保存到父类的偏移量(地址)。
调用父类(B/C)中继承的成员时,先通过虚基表中的偏移量找到父类(A),然后再找到该成员,而不是直接在父类(B/C)中找到该成员(A类成员)。
当继承的父,子类中存在同名的虚函数时,且参数相同,返回类型相同(协变除外)则就构成了多态。
注:子类可不用加virtual关键字,因为子类会继承父类的虚函数特性
协变—返回类型不相同,但必须是类的指针或引用。

10、代码实例:

1、继承方式:

using namespace std;

class A
{
public:
    A()
    {}

protected:
    void print()
    {
        cout << "输出" <<endl;
    }

private:
    int _a;
};

class B : public A
{
public:
    B()
    {}

    void change()
    {
        //this->_a  = 1;//错误父类私用成员,子类内不可见(不可访问)
        print();//代码段不属于某一个类,不使用this指针调用,父类的保护成员,子类内部可访问
    }

private:
    int _b;
};

void test()
{
    A a;
    B b;

    //b._a = 1;错误,父类私用成员不可见(不可访问)
    //b.print();//错误,保护成员外部不可见(不可访问)

    a = b;
    A & aa = b;//支持,说明未发生隐式类型转换
    //b = a;//父类对象不可赋给子类

    size_t d = 0;
    //int & t = d;//引用不支持,发生隐式类型转换
}

2、继承的构造函数问题:

//继承
#include<iostream>
using namespace std;

class A
{
public:


    //A(const int& a)
    //  :_a(new int(a))
    //{}

    //缺省构造函数 = 无参构造+有参构造
    A(const int& a = 0)
    :_a(new int(a))
    {
        cout << "构造A" << endl;
    }

    ~A()
    {
        cout <<"A"<< endl;
        delete _a;
    }

protected:
    int* _a;
};

class B : public A
{
public:

    //A类中无缺省的构造函数
    //B(const int& a,const int& b)
    //  :A(a) //正确
    //  ,_b(new int(b))
    //{}

    //B(const int& a,const int& b)
    //{
    //  A(a);//错误,在初始化列表时,会调用A类中的无参构造函数,但A类中无无参的构造函数,所以会报无默认的构造函数出错
    //  _b = new int(b);
    //}


    //A类有带缺省的构造函数时

    //B(const int& a,const int& b)
    //{
    //  A(a);//错误,在初始化列表时,会调用A类中的参构造函数,无参调用,实例化出a,此处再在调用父类构造,带参调用,又实例化出一个a,导致重定义问题
    //  _b = new int(b);
    //}

    //B(const int& a,const int& b)
    //{
    //  _a = new int(a); //错误,在初始化列表时,会调用A类中的参构造函数,无参调用,实例化处_a后,已对_a开辟空间,此处不可再开辟空间
    //  _b = new int(b);
    //  cout << "构造B" << endl;
    //}

    //B(const int& a,const int& b)//正确
    //{
    //  *_a = 1; //正确,在初始化列表时,会调用A类中的参构造函数,无参调用,实例化处_a后,此处进行初始化
    //  _b = new int(b);
    //  cout << "构造B" << endl;
    //}

    B(const int& a,const int& b)//正确
        :A(a)
        ,_b(new int(b))
    {
        cout << "构造B" << endl;
    }

    //~B()//自动先析构子类,后析构父类
    //{
    //  ~A();//错误,子类和父类析构函数,在底层上是同名的函数,即构成了重定义,子类隐藏了父类,导致递归出错。
    //  cout <<"B"<< endl;
    //  delete _b;
    //}

    //~B()//自动先析构子类,后析构父类
    //{
    //  A::~A();//错误,显示析构父类后,再析构子类,子类析构后会自动再析构父类,导致出错
    //  cout <<"B"<< endl;
    //  delete _b;
    //}

    ~B()//自动先析构子类,后析构父类---正确
    {
        cout <<"B"<< endl;
        delete _b;
    }

protected:
    int* _b;
};

void test3()
{
    B bb(0,0);
}

3、菱形继承

//菱形继承
#include<iostream>
using namespace std;

class A
{
public:
    A()
    {}

    void Funa()
    {
        cout << "Funa()" << endl;
    }

public:
    int _a;
};

class B : public A //公有继承A
{
public:
    B()
    {}

    void Funb()
    {
        cout << "Funb()" << endl;
    }

public:
    int _b;
};


class C : public A //单继承
{
public:
    C()
    {}

    void Func()
    {
        cout << "Func()" << endl;
    }

public:
    int _c;
};

class D : public B,public C //多继承
{
public:
    D()
    {}

    void Fund()
    {
        cout << "Fund()" << endl;
    }

public:
    int _d;
};

void test2()
{
    A aa;
    B bb;
    C cc;
    D dd;

    cout << "类的大小" << endl; //函数(代码段)和静态成员(静态区)不属于某一个对象,是所有对象共有的,所以不算做对象的大小。
    cout << "aa = " << sizeof(aa) << endl; //4
    cout << "bb = " << sizeof(bb) << endl; //4+4
    cout << "cc = " << sizeof(cc) << endl; //4+4
    cout << "dd = " << sizeof(dd) << endl; //8+8+4

    aa._a = 0;
    //dd._a = 10;//错误,D类继承有多个数据_a,存在二义性问题
    //dd.A::_a  = 1; //错误,A不是D的直接父类,所以A不算是D的父类,不可直接访问A类成员
    dd.B::_a = 1; //通过类域,指定访问B类中的_a成员,可解决二义性问题,但D类中有多个_a成员,存在数据冗余问题。
    dd.C::_a = 2; //D类中的两个_a成员数据不同
    dd._b = 3;
    dd._c = 4;
    dd._d = 5;
    cout << dd.B::_a << endl;
    cout << dd.C::_a << endl;
    cout << aa._a << endl;
}

4、菱形虚继承

//菱形虚继承

#include<iostream>
using namespace std;

class A
{
public:
    A()
    {}

    void Funa()
    {
        cout << "Funa()" << endl;
    }

public:
    int _a;
};

class B : virtual public A //公有继承A
{
public:
    B()
    {}

    void Funb()
    {
        cout << "Funb()" << endl;
    }

public:
    int _b;
};

//注意virtual关键字添加的位置,添加在继承公共父类处
class C : virtual public A //单继承
{
public:
    C()
    {}

    void Func()
    {
        cout << "Func()" << endl;
    }

public:
    int _c;
};

class D : public B,public C //多继承
{
public:
    D()
    {}

    void Fund()
    {
        cout << "Fund()" << endl;
    }

public:
    int _d;
};

void test2()
{
    A aa;
    B bb;
    C cc;
    D dd;
    //函数(代码段)和静态成员(静态区)不属于某一个对象,是所有对象共有的,所以不算做对象的大小。
    cout << "类的大小" << endl; 
    cout << "aa = " << sizeof(aa) << endl; //4
    cout << "bb = " << sizeof(bb) << endl; //4+4+4(虚基表)
    cout << "cc = " << sizeof(cc) << endl; //4+4+4(虚基表)
    cout << "dd = " << sizeof(dd) << endl; //12+12+4-4(A类大小),

    dd.B::_a = 0; //通过类域,指定访问B类中的_a成员,
    dd.C::_a = 1; //通过类域,指定访问C类中的_a成员,
    dd._a = 2;   //但实际都是直接对同一个父类A操作,结果值相同
    dd._b = 3;
    dd._c = 4;
    dd._d = 5;

    //注:B,C不直接继承A类,而是通过虚基表表找到A类,所以B,C共用的是一个A类(原A类),而不是各种独有。所以减去多统计的A类大小
    dd._a = 10;//正确,D类继承父类B,C,两个父类根据虚基表找到他们的公共父类A,他们都是直接对同一个父类A操作。所以D类实际只有在一个_a成员,从而解决二义性和数据冗余问题
    //dd.A::_a  = 1; //错误,A不是D的直接父类,所以A不算是D的父类,不可直接访问A类成员
    dd.B::_a = 1; //通过类域,指定访问B类中的_a成员,
    dd.C::_a = 2; //通过类域,指定访问C类中的_a成员,但实际都是直接对同一个父类A操作,结果值相同
    dd._b = 3;
    dd._c = 4;
    dd._d = 5;
    //对公共继承的父类数据操作,即直接对原父类中的数据操作,数据相同
    cout << dd.B::_a << endl;
    cout << dd.C::_a << endl;
    cout << aa._a << endl;//对aa对象操作,属于不同对象,所以数值不同
}

继承图解:
这里写图片描述

//5、无法继承的类
#include<iostream>
using namespace std;

class A
{
private: //将A的构造函数定义成私有的,使类不可被继承
    A()
    {
        cout << "构造A" << endl;
    }

public:
    static A* Get()
    {
        A();
    }

protected:
    int _a;
};

class B : public A
{
public:
    B() //不可调用A的构造函数,无法实例化出对象
    {
        cout << "构造B" << endl;
    }

    int add()
    {
        _a = 10;
        _b = 20;
        return _a+_b;
    }

protected:
    int _b;
};


void test4()
{

    B bb;
    A* a = A::Get(); //通过接口实例化对象
}

三、多态

一、先介绍一下虚函数:
1、实现:在类的成员函数前加关键字virtual,则这个成员函数变为虚函数
2、虚函数的重写:在子类中定义一个与父类完全相同的虚函数(函数名相同,参数相同,返回类型相同,协变除外),则子类会覆盖掉父类中的这个虚函数,称为重写
注:1、父类必须加关键字virtual,子类可不用加virtual关键字,因为子类会继承父类的特性
2、协变—-返回类型是对应类的的指针或引用(同为指针或引用)
3、静态成员函数不能定义的虚函数
4、只有类的成员函数才能定义成虚函数
5、类外定义不可加virtual关键字,类内声明时可加virtual关键字
6、构造函数不可为虚函数,析构函数最好定义成虚函数
原因:构造函数定义成虚函数,调用构造函数时会调用虚表,在虚表中找构造函数,但由于对象还未实例化,所以还未创建续表,导致找不到构造函数出错。

7、析构函数定义虚函数:父类的指针有可能指向的是子类的对象,若子类的析构函数不是多态,则只调用父类的析构,而不会调用子类的析构,导致子类的空间未释放,造成内存泄漏。
列:A * aa = new B; //父类指针指向子类的对象。
delete aa; //析构只调用父类的析构,导致子类内存泄露。
若将析构函数定义成虚函数,由于父类和子类的析构函数名实际是相同的,所以会构成重写。析构时根据对象调用对应的析构函数,从而避免内存泄漏问题。

8、内联函数不可定义成虚函数?
内联函数是在编译时进行展开的,虚函数的调用是在运行时动态绑定的,所以不可定义成虚函数。

9、静态成员函数不可定义成虚函数?
静态成员函数属于类的,不属于某个对象,是所有对象的共有,所以没必要在运行时联编,没必要定义成虚函数。

10、友元函数不可定义成虚函数?
友元函数不可被继承,不可继承的函数不可定义成虚函数。

其它:
1、含有虚函数的类:会创建一个虚表,用于存储虚函数的地址,虚表实际主要就是一个函数指针数组。
2、子类会继承父类的虚函数,会重写所有父类中相同的虚函数,但只将自己的虚函数写在最先继承的父类的虚表中。
3、类中不存虚表,而是存指向这个虚表的的指针。虚表存在代码段中,同类的所有对象共用一个虚表。

二、纯虚函数:
1、方法:在声明时将成员函数的虚函数后写 = 0,则该成员函数即为纯虚函数。
注:写 = 0;的意思是将函数的地址初始化为0,告诉编译器不为该函数编制,从而阻止该类的实例化。

2、含有纯虚函数的类称为抽象类。抽象类不可实例化出对象,纯虚函数在派生类中重新定以后,派生类才能实例化出对象,父类还是不可实例化出对象。
目的:让子类继承并实现他的接口方法
作用:接口与实现分离,不仅把数据成员隐藏,还把实现完全隐藏,只留一些接口给外部调用。从而即使将来实现改变,接口可以保持不变,向后兼容。

这种彻底的封装思想,体现了面向对象的动态绑定技术,主要引用于COM,CORBA等体系结构。
函数都是public的纯虚函数的类称为接口类。

三、多态:

1、多态就是多种形态,C++的多态分为静态多态和动态多态
静态多态:如重载,在编译时进行决议(联编),将函数地址放到函数调用处。
动态多态:通过继承重写基类的虚函数实现的多态,在运行时进行决议(联编)。
多态的虚函数存放在虚表中,虚表实际就是一个函数指针数组,由于数组下标只有在运行时才能确定。所以重写是动态多态。

2、作用:同一接口实现不同的功能,调用结果与对象有关。根据对象调用各自对应的方法,子对象调用子类的,父类对象调用父类的。
不是多态时,调用与类型有关。根据类调用对应的方法。

3、当使用基类的指针或引用调用重写的虚函数时,指向父类调用的就是父类的虚函数,指向子类调用的就是子类的虚函数。

必要条件:
1、调用的必须是子类重写了父类的虚函数
2、必须是父类的指针或引用。(根据所指向的类或引用的对象调用,子类对象调用子类的,父类对象调用父类的。)

继承体系中同名的函数:
一、重载:
1、在同一个作用域
2、函数名相同,参数不同
3、返回值可不同

二、重写(覆盖)
1、在不同的作用域(分别在子类和父类中)
2、函数名相同,参数相同,返回值类型相同(协变除外)
3、访问修饰符可不同
4、基类必须有virtual关键字修饰

三、重定义(隐藏)
1、不在同一作用域(分别在子类和父类中)
2、函数名相同
3、在子类和父类中,不构成重写即为重定义

代码实例:
1、菱形虚继承+虚函数

//菱形虚继承+虚函数

#include<iostream>
using namespace std;

class A
{
public:

    A()
    {
        cout <<"A构造"<< endl;
    }

    ~A()
    {
        cout <<"B析构"<< endl;
    }
    virtual void Fun1()
    {
        cout << "A.Fun()" << endl;
    }

    void Funa()
    {
        cout << "a.Funa()" << endl;
    }
public:
    int _a;
};

class B : virtual public A
{
public:
    B()
    {
        cout <<"B构造"<< endl;
    }

    virtual void Fun2()
    {
        cout << "B.Fun()" << endl;
    }

    void Funb()
    {
        cout << " b.Funb()" << endl;
    }

    ~B()
    {
        cout <<"B析构"<< endl;
    }

public:
    int _b;
};


class C : virtual public A
{
public:
    C()
    {
        cout <<"C构造"<< endl;
    }

    virtual void Fun3()
    {
        cout << "c.Fun()" << endl;
    }

    void Funb()
    {
        cout << " c.Funb()" << endl;
    }

    ~C()
    {
        cout <<"C析构"<< endl;
    }

public:
    int _c;
};

class D : public B,public C
{
public:
    D()
    {
        cout <<"D构造"<< endl;
    }

    virtual void Fun()
    {
        cout << "D.Fun4()" << endl;
    }

    void Funb()
    {
        cout << " d.Funb()" << endl;
    }

    ~D()
    {
        cout <<"D析构"<< endl;
    }

public:
    int _d;
};

void test5()
{
    A aa;
    B bb;
    C cc;
    D dd;
    dd._a = 1;
    dd._b = 2;
    dd._c = 3;
    dd._d = 4;

    cout << sizeof(aa) << endl; //8
    cout << sizeof(bb) << endl; //20
    cout << sizeof(cc) << endl; //20
    cout << sizeof(dd) << endl; //36 = 20+20-8+4 ,虚继承B/C占用同一个父类A,-8
}

图解:
这里写图片描述

2、菱形虚继承+多态

//菱形虚继承+多态

#include<iostream>
using namespace std;

class A
{
public:

    A()
    {
        cout <<"A构造"<< endl;
    }

    ~A()
    {
        cout <<"B析构"<< endl;
    }

    virtual void Fun()
    {
        cout << "A.Fun()" << endl;
    }

    virtual void Fun1()
    {
        cout << "A.Fun1()" << endl;
    }


    void Funa()
    {
        cout << "a.Funa()" << endl;
    }
public:
    int _a;
};

class B : virtual public A
{
public:
    B()
    {
        cout <<"B构造"<< endl;
    }

    virtual void Fun()
    {
        cout << "B.Fun()" << endl;
    }

    virtual void Fun2()
    {
        cout << "B.Fun()" << endl;
    }

    void Funb()
    {
        cout << " B.Funb()" << endl;
    }

    ~B()
    {
        cout <<"B析构"<< endl;
    }

public:
    int _b;
};


class C : virtual public A
{
public:
    C()
    {
        cout <<"C构造"<< endl;
    }

    virtual void Fun()
    {
        cout << "C.Fun()" << endl;
    }

    virtual void Fun3()
    {
        cout << "C.Fun()" << endl;
    }

    void Funb()
    {
        cout << " c.Funb()" << endl;
    }

    ~C()
    {
        cout <<"C析构"<< endl;
    }

public:
    int _c;
};

class D : public B,public C
{
public:
    D()
    {
        cout <<"D构造"<< endl;
    }

    virtual void Fun()
    {
        cout << "D.Fun()" << endl;
    }

    virtual void Fun4()
    {
        cout << "D.Fun4()" << endl;
    }

    void Funb()
    {
        cout << " d.Funb()" << endl;
    }

    ~D()
    {
        cout <<"D析构"<< endl;
    }

public:
    int _d;
};

void test5()
{
    A aa;
    B bb;
    C cc;
    D dd;
    dd._a = 1;
    dd._b = 2;
    dd._c = 3;
    dd._d = 4;

    cout << sizeof(aa) << endl; //8
    cout << sizeof(bb) << endl; //24
    cout << sizeof(cc) << endl; //24
    cout << sizeof(dd) << endl; //40 = 24+24-8-4+4 //虚继承共用一个父类A,所以-8,父类B,C用同一个占位地址-4。
}

图解:
这里写图片描述

四、三大特性总结
封装:
1、封装:二中方法(1、类将数据和函数捆绑,2、访问限制符限制)
2、作用:信息隐藏,只提供必要接口,防止外部干扰

继承:
1、方式:public(is-a) protected,private(has-a)
2、作用:通过子类继承父类实现代码复用。
分类:
单继承—只有一个直接父类
多继承—有多个直接父类
菱形继承—单继承和多继承的复合
缺陷:数据二义性,数据冗余
解决:虚继承
虚基表
1、存放到虚表的的偏移量
2、存放到父类的偏移量

多态:
分类:静态多态,动态多态
作用:一个接口实现多种功能,根据对象调用对应的实现函数
必要条件:
1、父,子类中必须有完全相同的虚函数。
2、必须是父类的指针或引用。
虚函数:
使用关键字virtual修饰的函数
虚表
存放虚函数的地址(函数指针数组)
父类有虚表时不创建虚表,有多大个父类时,每个父类都会形成多态,但只将自己的虚函数放在最先继承的父类中
最好为虚函数:析构函数
不可为虚函数:构造,静态,友元,内联。
纯虚函数:在虚函数后写 = 0;函数地址初始为0,告诉编译器不为该函数编址从而使类不可实例化出对象。
含有纯虚函数的类称为抽象类,若继承方式为public的类称为接口类。

如何定义一个不可被继承的类?
解:将类的构造函数定义成私有的。
1、类的私有成员只能在类内可访问,子类不可见。
2、子类是合成的,调用子类的构造函数时会先调用父类的构造函数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值