IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++入门---类和对象(上) -> 正文阅读

[C++知识库]c++入门---类和对象(上)

类的引入

我们在c语言当中学习过这么一个关键字struct,当时我们说c语言给的那些类型无法处理一些非常复杂的对象,比如说我想创建一个变量来描述一个学生的基本信息,那我们c语言的内置类型:int, double,float等等都无法做到来描述一个学生的基本信息,所以我们c语言就有了一个struct这么个关键字,他可以把多个不同的类型放到一块,并且对其重新取个名字,这样我们就可以用来描述更加复杂的对象,比如说下面的代码:

struct student
{
	int age;
	char name[20];
	char id[20];
};

这样我们就创建出来一个可以描述学生的结构体类型,那么我们就可以通过该类型来创建出来变量用来描述学生,比如说我们下面的代码:

int main()
{
	struct student ycf = { 19,"yechaofan","123456789" };
	printf("学生的年龄为:%d\n", ycf.age);
	printf("学生的姓名为:%s\n", ycf.name);
	printf("学生的学号为:%s\n", ycf.id);
	return 0;
}

我们这里就用该结构体类型创建出来一个变量 名为ycf,然后将该变量的内容进行初始化,并且打印,那么这样的话我们发现运用struct这个关键字就可以很好的描述一些比较复杂的对象,但是我们的c++在此之上就做出了一些改变,我们c语言中的struct只能将一些内置类型进行整合放到一起,不能加其他的东西,但是c++却可以,c++中的struct还可以在此之上来定义一些函数,比如说我们之前数据结构中学的栈,我们是不是创建了一个结构体变量和对应的函数,但是函数和这个结构体变量他是分开的,不在一起,struct里面只有用来描绘栈的一些变量,那么在c++里面我们就可以将这些函数和变量全部都放到struct里面比如说下面的代码:

typedef int DataType;
struct Stack
{
	void Init(size_t capacity)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(const DataType& data)
	{
		// 扩容
		_array[_size] = data;
		++_size;
	}
	DataType Top()
	{
		
			return _array[_size - 1];
	}
	void Destroy()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
	DataType* _array;
	size_t _capacity;
	size_t _size;
};

那么我们就不再叫这里的struct为结构体,而是称之为类。

c中的struct和c++中的struct的一些区别

第一点:
在c语言中我们在结构体里面顶一个与该结构体有关的一些变量的时候我们得加上struct这个关键字,比如说我们在学习链表的时候得在结构体里面创建一个结构体的指针,用来方便我们找到下一个元素,比如说我们下面的代码:

struct ListNode
{
	struct ListNode* next;
};
int main()
{
	return 0;
}

我们这里就创建了一个结构体的指针,但是我们在创建这个结构体指针的时候他是必须得加上struct这个关键字的,如果我们不加这个关键字的话他就会报错,比如说我们下面的代码:

struct ListNode
{
	ListNode* next;
};
int main()
{
	return 0;
}

我们将这个代码运行一下就可以发现这里的报出了错误:
在这里插入图片描述
就算我们用typedef将这里的struc ListNodet改成ListNode也是不可以的,比如说我们下面的代码:

typedef struct ListNode
{
	 ListNode* next;
}ListNode;
int main()
{
	return 0;
}

他依然会报出同样的错误:
在这里插入图片描述

但是在我们c++当中就可以不加这个struct,我们将.c改成.cpp再运行一下就可以发现我们这里没有报出错误:
在这里插入图片描述
当然我们这里跟c语言一样加上struct也是可以的。
第二点:
c语言在用结构体类型创建结构体变量时,如果我们不用typedef进行重命名的话那也是得加上struct的,比如说我们下面的代码:

struct student
{
	int age;
	char name[20];
	char id[20];
};
int main()
{
	struct student ycf ;
	return 0;
}

如果我们不加这个struct的话就会报出错误:
在这里插入图片描述
但是我们的c++在创建类的时候就可以不加这个struct但是依然可以正常地运行,那么这也是两者地区别之一:
在这里插入图片描述

class的介绍

在c++当中我们喜欢用class来代替struct,所以就有了我们这里的class关键字,class的基本格式就是这样:
在这里插入图片描述
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。

类的两种定义方式

第一种:

  1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。那这句话的意思就是就算我们不加inline也可能会当成内联函数。比如说我们下面的代码就是声明和定义全部都放在类体中:
#include<iostream>
using namespace std;
class person
{
	void showinfo()
	{
		cout << _name << "-" << _sex << "-" << _age <<endl;
	}
	char* _name;
	char* _sex;
	int _age;
};

第二种:

第二种定义的方式就是: 类声明放在.h文件中,成员函数定义放在.cpp文件中。比如说我们下面的代码:

//.h文件
#include<iostream>
using namespace std;
class person
{
	void showinfo();
	char* _name;
	char* _sex;
	int _age;
};
//.cpp文件
#include"源1.h"
void person::showinfo()
{
	cout << _name << "-" << _sex << "-" << _age << endl;
}

那这就是第二种类的定义方式,但是这种定义方式需要大家注意的一件事就是成员函数名前需要加(类名::)因为这样我们才知道你定义的是哪个类中的函数,而且不同的类中可以定义同名的函数,所以我们这里加个(类名::)就可以更好的来进行区分,并且这些同名的函数并不会构成重载因为构成重载的前提条件是作用域相同,而这些不同类中的同名函数他们的作用域并不相同所以也就不会构成重载。那我们这里定义和声明分开的好处是什么呢?那就是方便大家阅读,因为我们往后遇到的类里面的函数和变量会特别的多,如果我们定义和声明的放在一起的话,那阅读的速度会非常的慢,比如说我就想看看这个类中有哪些函数,并不想知道这些函数是如何实现的,那如果我定义和声明不分开的话,那我是不是得看好久啊,如果分开的话那我看到的全是声明一个定义没有,那么这样就可以大大的提高我们的阅读性,所以这就是我们的定义与声明分开的一个好处。

类的访问限定符及封装

什么是访问限定符

c++实现封装的方式::用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。那么我们这里可以这样来理解这句话就是使用访问限定符可以让类中的一些东西只能在类中使用,也可以让类中的一些东西可以在该类的外面来使用。那么访问限定符就可以分为下面三种:
在这里插入图片描述
我们这里就主要介绍public和private这两个访问限定符。

访问限定符的说明

  1. public修饰的成员在类外可以直接被访问。
  2. private修饰的成员在类外不能直接被访问,但是在类里面可以直接访问。

这两个就非常好理解,两个对立面嘛 一个想让外面使用一个不想让外面使用,我们接着来看看第三点:

  1. 访问权限作用域从该访问限定符出现的位置开始一直到下一个访问限定符出现时为止
    比如说我们下面的代码:
class lei
{
public:
	void func1();
	void func2();
	void func3();
private:
	void func4();
	void func5();
	void func6();
public:
	void func7();
	void func8();
	void func9();
};

那么这里的第一个public能够修饰的范围就是到下一个访问限定符出现为止,也就是func3下面的private,所以func1,func2,func3就被第一个public来修饰,同样的道理第二个private修饰的范围就是到第三个public出现为止,那么func4,func5,func6就被private修饰,接下来就轮到了第三个限定符public,那他能够修饰的范围也是到下一个访问限定符出现为止,但是他下面没有限定符了啊,那么这里就得用到我们的第四个规则:
5. 如果后面没有访问限定符,作用域就到 } 即类结束。
所以第三个限定符public就修饰到结尾为止,func7,func8,func9就被第三个public修饰。好看到这里想必大家应该能够理解我们这里的限定符的作用,以及使用方法,那我们将这里的函数进行一下修改一下减短篇幅看看这里要是使用了private修饰的函数会怎么样:

#include<stdio.h>
class lei
{
public:
	void func1()
	{
		printf("public");
	}
private:
	void func4()
	{
		printf("private");
	}
public:
	void func7()
	{
		printf("public");
	}
};
int main()
{
	lei s1;
	s1.func1();
	return 0;
}

那么我们将这个代码运行一下看看执行的结果如何:
在这里插入图片描述
我们发现这里如果是public修饰的函数的话就可以正常的打印 ,但是如果我们这里将func1改成func4然后再运行就会发现报出了错误:
在这里插入图片描述
通过报出的错误我们可以发现这里错误的原因就是无法访问private里面的成员,那么看了这个例子大家应该能够明白我们这里的private和public的修饰的规则和与之修饰所带来的影响,那么这里大家来思考一个问题就是我们的类既可以用struct来定义,也可以使用class来定义,那这两个定义的方式有什么不同呢?那我们这里的解答就是C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是private。就说当我们不人为添加限定符的时候struct里面的内容都是可以在类外访问的,而class里面的内容都是不可以在类外访问的。

封装

我们首先来看看封装的定义是什么?封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。那我们这里可以通过我们平时使用的电脑来理解这里封装的意思,我们的电脑是一个非常复杂的东西,他里面有许多许多的配件,有主板,硬盘,散热装置,显卡,cpu,等等复杂的原件组成,但是这么复杂的一个东西提供给用
户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。因为对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。那我们c++里的封装也是如此:可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。那这么看的话我们的类就好比整个电脑,类里面对外公开使用的函数和数据就好比电脑里面的键盘,鼠标显示器开关键,而类里面的不让对外使用的函数和数据就相对电脑里面的cpu,显卡内存等等核心软件,那为什么我们的c++要搞一个封装出来呢?那我们这里就得回想一下我们之前学的数据结构我们在实现顺序表的时候是先创建一个结构体,在该结构体里面放置一些变量来记录我们的该顺序表的一些数据比如说我们下面的代码:

typedef struct SeqList
{
	SLDataType* a;//指针指向的开辟的内存的地址
	int size;//当前链表中含有的元素个数
	int capacity;//链表的容量
}SL;

大家可以看到我们这个结构体里面有一个变量的名字叫做size,并且我们的注释旁边也解释了这个变量的作用就是记录当前链表中含有的元素个数,但是我们在实现顺序表的时候写过这么个函数就是得出我们当前顺序表元素的个数,但是有小伙伴们就觉得这个函数十分多余,我直接通过结构体访问这里的size不就可以得到这个顺序表的元素个数了吗?我何必非要通过函数来得到呢?那这里大家要是有疑惑的话我们可以来看看下面的代码,这个代码有两块,但是实现的功能都是一样的就是将顺序表进行初始化,并且实现顺序表的尾插,我们首先来看第一个实现方式:

void SLInit(SL* psl)
{
	assert(psl);
	psl->a = NULL;
	psl->capacity = psl->size = 0;
}
void SLInsertBuck(SL* psl, SLDataType x)
{
	assert(psl);
	SLCheckCapacity(psl);
	psl->a[psl->size] = x;
	psl->size++;
}

我们再来看看第二个顺序表的实现方式:

void SLInit(SL* psl)
{
	assert(psl);
	psl->a = NULL;
	psl->capacity = 0;
	psl->size =-1}
void SLInsertBuck(SL* psl, SLDataType x)
{
	psl->size++;
	assert(psl);
	SLCheckCapacity(psl);
	psl->a[psl->size] = x;
}

大家有没有发现其中的区别,如果说我们结构体中的size表示的是顺序表中的元素的个数的话,那好像只有第一种实现方式是对的,我们第二种实现方式中的size并不表示顺序表中的元素个数,但是如果我们不知道这个顺序表的作者是如何实现的,而我们却擅自的通过结构体里的size来达到我们的目的的话,是不是就很容易的出错啊,那么这里就可以体现出我们封装的重要性,我们可以将这里的size等一些不希望使用者访问到的数据数据全部用private来修饰,这样使用者就无法访问的到,只能通过我公开的函数和数据来达到他的目的,因为我是这些数据和函数的创始人我是知道正确的实现方式的,所以我给你们这些函数的正确性是有保障的,所以这样的话使用者在使用的过程中出现bug的概率会更低,那么这就是我们封装的意义。希望大家理解。

类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。比如说我们下面的代码:

class Person
{
public:
 void PrintPersonInfo();
private:
 char _name[20];
 char _gender[3];
 int  _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
 cout << _name << " "<< _gender << " " << _age << endl;
}

类的实例化

用类类型创建对象的过程,称为类的实例化
第一点:
类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;比如说我们的造房子,在把房子造出来之前是不是会有很多的设计图纸啊,那么这些设计图纸就是我们的类,而这些图纸他们是不会占我们的地球表面空间的,但是我们通过这些图纸造出来的一些大大小小的房子这是得占地球的表面空间的,那么我们这里造出来的房子就是我们所谓的类的实例化,他是会占内存空间的,比如说我们下面的代码:

class Person
{
public:
 	void PrintPersonInfo()
 	{
 	 cout << _name << " "<< _gender << " " << _age << endl;
 	 }
 	char _name[20];
 	char _gender[3];
 	int  _age;
};
int main()
{
 	Person._age = 100;   // 编译失败:error C2059: 语法错误:“.”
	 return 0;
}

我们来看看这个代码的运行结果:
在这里插入图片描述
那么我们这里错误的原因就是因为person只是一个类,他并没实例化所以我们这里是不能对其进行赋值等操作的,但是我们将其进行实例化,用这个类创建了一个对象,对这个对象进行操作的话那是可以的,比如说我们下面的代码:

int main()
{
    Person s1;
    s1._age = 100;
    return 0;
}

我们再运行一下就会发现没有报错:
在这里插入图片描述
第二点:
一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量,那这个就非常的容易理解嘛,一份图纸可以造出很多个相同的东西嘛,那这也是相同的道理嘛。

类对象大小的计算

我们之前学习c语言的结构体时介绍过如何计算结构体的大小,但是我们c语言的结构体里面都是一些简单的变量,并没函数,所以我们内存对其原则可以很好的计算出结构体的大小,但是我们的c++的类里面是有函数的,那我们的内存对齐原则还适用吗?如果实用的话这些函数所占的空间是多少呢?她的对齐数又是多少呢?那么带着这些疑问我们来看看类中对象大小的三种可能计算方式:
第一种:对象中包含类的各个成员
那这种储存方式就是在实例化对象的时候即存变量也存函数
在这里插入图片描述
那么这种储存方式是有缺陷的,缺陷就是:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。那么如何解决呢?那么这里就有了第二种储存方式:
第二种:代码只保存一份,在对象中保存存放代码的地址
在这里插入图片描述
这种储存就是存一些变量的同时还要储存一个地址,这个地址指向的就是这个类中函数的位置,我们在调用函数的时候可以通过这个地址来找到这些函数当然我们这里还有第三种储存方式。
第三种:只保存成员变量,成员函数存放在公共的代码段
在这里插入图片描述
第三种存储的方式就有点奇怪,他的存储方式跟c语言得结构体得方式差不多里面没有给可以找到函数的路线,而且他把那些函数都放到另外的一个空间里面去了在公共代码区,那我们这里有三种方法我们的编译器到底会采用哪种办法呢?那么我们就可以用下面的代码来进行验证:

#include<iostream>
class A1
{
public:
	void f1() 
	{
	}
private:
	int _a;
};
int main()
{
	std::cout << sizeof(A1);
	return 0;
}

我们将这个代码运行一下来看看结果如何:
在这里插入图片描述
我们发现这里打印的结果为4,而一个整型的大小也为4,那也就是说我们这里采用的第三种储存方式,那这里我们如何来理解呢?这里之所以采用第三种方式来进行储存的原因就是因为,我们的编译器在底层实现的时候是知道该类的函数储存在哪的,不需要我们额外的添加一些路线和地址来进行指引,所以这里也就采用了第三种存储方式,那也就说我们以后在计算类的大小的时候就采用和结构体一样的内存对齐原则就可以了。
结构体内存对齐规则

  1. 第一个成员在与结构体偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的对齐数为8
  3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

看到这里大家来看一个问题:

// 类中既有成员变量,又有成员函数
class A1 
{
public:
    void f1(){}
private:
    int _a;
};
// 类中仅有成员函数
class A2 
{
public:
   void f2() {}
};
// 类中什么都没有---空类
class A3
{
};
int main()
{
	std::cout << sizeof(A1) << std::endl;
	std::cout << sizeof(A2) << std::endl;
	std::cout << sizeof(A3) << std::endl;
	return 0;
}

那我们这个代码的运行结果是什么呢?有小伙伴一看就说第一个有个int和函数但是我们自存变量的话,那这样的话他的大小不就是4嘛,第二个类只有函数,但是我们存储的只有变量所以为0,第三个类啥都没有那肯定为0,有些小伙伴的肯定是这么想的,但是我们将代码运行一下就会发现我们的结果是这样的:
在这里插入图片描述
我们的答案是4 1 1 ,有些小伙伴可能就疑惑了为什么一个类中什么都没有他的结果还是1 呢?那这里我们就得想一个东西就是如果我们一个类中什么都没有的话,那你通过这个类创建出来一个对象的话,如果这个对象的大小为0 的话,那你如何来证明这个对象的存在呢?你说你创建了一个对象,但是在内存中完全找不到这个对象的存在,那这是不是就出问题了,所以即使我们这里类里面什么都没有我们这里还是得给他的大小为1 ,这个1就是一个标记的作用表明他的存在,结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

this指针

我们这里先来看看下面的一段代码:

#include<iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year; 
	int _month; 
	int _day; 
};
int main()
{
	Date d1, d2;
	d1.Init(2022, 1, 11);
	d2.Init(2022, 1, 12);
	d1.Print();
	d2.Print();
	return 0;
}

我们这里创建了一个日期的类,然后这个类里面有两个函数一个函数的功能是对这个类里面的数据进行初始化,另外一个函数的功能是就是打印这些数据,那么我们下面就用这个类来创建出来两个对象d1和d2,并且调用init函数对这两个类进行初始化,再调用类里面的print函数来打印这里的数据,那么我们这里运行一下代码就可以看到这里确实打印出来数据:
在这里插入图片描述
但是这里大家有没有想过一个问题就是,虽然我们这里创建出来了两个类但是他们的函数放到的地方都是一模一样的啊,也就是说d1和d2他们都是公用的同一个函数,那我们这里在初始化的时候他是如何知道的这里要初始化d1的这个类呢?我们在打印的时候他又是如何知道这里要打印的是d1的数据而不是d2的呢?那么我们的c++为了解决这个问题就引入了this指针来解决这个问题:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。比如说我们这里的打印函数我们看到的是这样:

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	d1.Print();

我们的print函数没有传任何的参数,但是我们的编译器在对其进行编译的时候就会对其进行修改,改成这样:

void Print(Data*const this)
{
	cout <<this-> _year << "-" << this->_month << "-" << this->_day << endl;
}
d1.Print(&d1);

这样的话我们就可以知道这里是对哪个类进行操作,要打印的是哪个类的数据了,那么这里还有几个关于this指针的性质:

  1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
  2. 只能在“成员函数”的内部使用
  3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针,并且this指针存在栈帧中或者寄存器中。
  4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递,而且我们还可以在函数里面人为使用这个this指针。

那么看到这里大家最后再来想一个问题就是当我们的类没有进行实例化的话那可以使用这个类里面的函数吗?答案是不行的,虽然我们这里创建类的时候就已经开辟了空间给这个类的函数,但是我们依然无法来调用这个类里面的函数,因为当我们没有实例化的时候我们这里是不知道传什么给我们这里的this指针的,所以是不行的。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:35:33  更:2022-10-31 11:36:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 4:01:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码