目录

  • 1. 面对对象的语言(object-oriented programming (OOP))
    • a. 为什么要使用面对对象的语言?
    • b. 对象 (Object) 是干什么的 ?
  • 2. 类(Class)
    • a. 类是什么?
    • b. 类的定义
    • c. 访问器与写值器(Accessors and Mutators)
    • d. 实例化(Instance)
    • e. 访问类的成员
    • f. 类的封装
      • 为什么要这么做?
    • g. 类中的内联函数成员 (Inline function)
    • h. 构造函数(Constructors)
    • i. 解构函数(Destructors)
  • 3. 继承(Inheritance)
    • a. 继承
    • b. 多继承(Multiple inheritance)
    • c.多重继承(Hierarchies)
  • 4. 重载、覆写与重定义(Overloaded, Overridden, Redefined)
    • a. 重载(Overloaded)
    • b. 重写(Overridden)
    • c. 重定义(Redefine)
  • 5. 虚函数和纯虚函数(Virtual function/Pure virtual function)
    • a. 虚函数(Virtual Function)
    • b. 纯虚函数(Pure virtual function)
  • 6. 抽象基类(Abstract base class)

【PS】本篇文章基本上用于自己学习的备份。如果有任何理解有问题的地方请在评论中指出,非计算机专业学生,错误还请多多谅解,谢谢!

本篇文章的引文基本均出自Starting Out with C++ From Control Structures Through Objects 8th Global Edition 如果有兴趣的可以去自己学习研究。

1. 面对对象的语言(object-oriented programming (OOP))

与C语言不同,C++是面对对象的语言。与其相对的,是面向过程的语言©。顾名思义,面对对象的语言是围绕"对象(Obejct)"进行的编程。

a. 为什么要使用面对对象的语言?

Procedural programming has worked well for software developers for many years. However, as programs become larger and more complex, the separation of a program’s data and the code that operates on the data can lead to problems. For example, the data in a procedural program are stored in variables, as well as more complex structures that are created from variables. The procedures that operate on the data must be designed with those variables and data structures in mind. But, what happens if the format of the data is altered? Quite often, a program’s specifications change, resulting in redesigned data structures. When the structure of the data changes, the code that operates on the data must also change to accept the new format. This results in additional work for programmers and a greater opportunity for bugs to appear in the code.

面对过程的语言对于数据是直接通过过程(procedure/method)对数据进行处理。这样带来的结果是,如果数据的格式发生了改变,则整个处理过程的框架都会发生改变,修改这整个过程框架就会产生漏洞。下图是面对过程的语言编程的大致架构。

面对对象的语言则是通过接口与外部进行链接,因此如果出现原始的数据结构改变时,外部的整体框架并不会发生改变,只需要改变这一个对象即可,因此大大减少了维护的复杂度。在大型的项目和复杂额程序中,OOP的优势即可显现。下图是面对对象的语言的大致架构。

b. 对象 (Object) 是干什么的 ?

An object is not a stand-alone program, but is used by programs that need its service. For example, Sharon is a programmer who has developed an object for rendering 3D images. She is a math whiz and knows a lot about computer graphics, so her object is coded to perform all the necessary 3D mathematical operations and handle the computer s video hardware. Tom, who is writing a program for an architectural firm, needs his application to display 3D images of buildings. Because he is working under a tight deadline and does not possess a great deal of knowledge about computer graphics, he can use Sharon s object to perform the 3D rendering.

对象不是一个独立的程序,而是被需要它服务的程序所使用。

例如,Sharon是一名程序员,她开发了一种用于渲染3D图像的对象。她是数学天才,对计算机图形学非常了解,所以她的目标是执行所有必要的3D数学运算,并处理计算机的视频硬件。汤姆正在为一家建筑公司编写程序,他需要他的应用程序来显示建筑物的3D图像。因为他的工作时间很紧,而且没有大量的计算机图形学知识,所以他可以使用Sharon的对象来执行3D渲染。

因此对象是服务于程序的。

2. 类(Class)

a. 类是什么?

A class is code that specifies the attributes and member functions that a particular type of object may have. So, a class is not an object, but it is a description of an object.

意思是:类是指定特定类型对象可能具有的属性和成员函数的代码。所以,类是一个描述对象的东西,而非对象本身。

形象一点的说法是,”类“就好比一个个房子,它可以供人居住和使用(供程序调用)。而”对象“就是里面的客厅,厨房,卧室等等房间(有各自的功能)。这些房间都有特定的用处,整个房子也是由这些有用的房间组成的。当然,这个房子里有些地方是可以让人随意进出的(类中的对象是公共的);也有些地方是不能上锁的(类中的对象是可以私有的)。

因此,在类中,包含有”成员变量(member variable)“和”成员函数(member function)“。每个成员有有可能是公共的/外部可访问的 (public) 或者是自己的/不可外部访问的 (private)。

b. 类的定义

在C++中,最简单的定义一个类:

class class_name
{declaration;// ... more declarations// may follow...
};//例如
class Rectangle
{double width;double length;
};
//有分号结尾 跟struct一样

但是对于类而言,为了数据的保护,会分为两种类型,private和public,这两种类型中,public是可以通过外部直接访问的成员,而private是不可以外部直接访问的,但是类的内部可以访问和调用(使用类内其他成员调用private成员)。

对于类里直接定义的变量来说,默认为private。因此,为了注明每一个成员是private还是public的,我们一般这样定义class:

class ClassName
{private:// Declarations of private// members appear here.public:// Declarations of public// members appear here.
};//例如
class Rectangle
{public:void setWidth(double);void setLength(double);double getWidth() const;double getLength() const;private:double width;double length;
};

其中,在函数后面添加const,意思是在这个函数不会更改存储在调用对象中的任何数据。例如这种函数一般只输出等。

如果成员函数需要输入,则只需在括号内添加数据类型即可。例如要两个输入:

void setWidth(double, double)

通常情况下,为了类的整洁和提高代码的可读性,一般会在类中先定义,然后再类外写对应的函数。

void Rectangle::set Width(double w)
{width = w;
}void Rectangle::getWidth() const
{cout<<width<<endl;
}

先声明函数,然后再写作用域(是那个类的)最后写出里面的哪个函数。
其中" :: "这个符号叫生存空间解析操作符 (scope resolution operator),表示的是”域“,用于说明这是哪个类下的成员。通俗的说,就是说明了是哪个房子的卧室一样(也可以用于区别不同类的重名函数,这个在一个子类继承多个父类,但是包含同名的函数时可以使用。)

c. 访问器与写值器(Accessors and Mutators)

【PS】由于是国外原著,因此对应得中文并不是很明确。如果知道准确的英文对应的中文请帮我改正,谢谢。

对于一个类而言,习惯上我们将所有变量成为private,然后通过相关的成员函数来读和写内部的变量。

从类中获取值但是不改变值得成员函数称之为Accessor(一般函数是const的)
在成员变量中存储值或以其他方式更改成员变量值的成员函数称为Mutator

为什么我们需要将有变量隐私并且设计看起来毫无作用的访问器和写值器这种多此一举的函数?
在学习编程的过程中,用户是自己,所以看起来像是向自己隐藏数据。但是,如果你在工业中编写软件,你创建的类将被用作大型软件系统的组件; 不是你自己或者熟人使用你的类。所以,通过隐藏类的数据并只允许通过类的成员函数访问它,这就可以更好地确保类将按照自己的预期进行操作。

下图就是将类中最重要的两个参数设置为private,并使用Accessor和Mutators去改写和访问:

d. 实例化(Instance)

定义了一个类就好比拿到了一张蓝图,但是蓝图终究是一张图纸,现在我们需要依据这个蓝图造出真实的房子,这就是实例化。

例如,现在我们定义了一个类叫做"Rectangle",现在我们想获得一个实例:

Rectangle box;

以类的名字当作一种数据类型,创建了一个名为box的静态Rectangle类。

如果要一个指针来实例化的话,则有两种方法:

1.
Rectangle box;             //先定义一个实例
Rectangle *ptr = &box;     //再定义一个这个类的指针,指向当前的类2.
Rectangle *ptr = nullptr;  //先定义一个空指针
ptr = new Rectangle;       //自动分配内存生成一个类的指针
delete ptr;                //不用后可以删除这个指针

当然,更好的是去使用unique_ptr,意味着只有一个指针能指向当前的类,这样对于数据的保护性更好。

#include<memory>    //使用memory库
unique_ptr<Rectangle> rectanglePtr(new Rectangle);
//建立一个名为rectanglePtr的指针

e. 访问类的成员

实例化了一个类,如何去访问里面的成员?

对于一个静态的实例,通常用点来访问,例如:

box.setWidth(15);

如果定义了一个对应的指针,通常用’->'来进行访问

Rectangle box;
Rectangle *ptr = &box;
ptr->setWidth(15);

f. 类的封装

对于软件工程(Software Engineering)来说,将类通常这么进行封装:

  1. 将类的定义放在自己cpp文件所对应的.h头文件中,这个称之为类规范文件(class specification file)。
  2. 将类的成员函数第一在同一名字的cpp文件下,这个称之为类实现文件(class implementation file)。
  3. 最后在总程序中include其对应的h文件即可使用这个类,类的h文件和cpp文件通常会自行链接。

例如,刚刚的类,我们可以尝试分别创建h文件和cpp文件。在Rectangle.h文件中:

#ifndef RECTANGLE_H
#define RECTANGLE_H// Rectangle class declaration.
class Rectangle
{private:double width;double length;public:void setWidth(double);void setLength(double);double getWidth() const;double getLength() const;double getArea() const;
};
#endif

可以看到,头文件中只定义了类,但内部的成员函数并未被定义。

然后,在对应的cpp文件中:Rectangle.cpp

#include "Rectangle.h" //先要包含头文件
#include <iostream>
void Rectangle::setWidth(double w)
{if (w >= 0) width = w;else{cout << "Invalid width\n";exit(EXIT_FAILURE);}...
}

在对应的cpp文件中,只定义其成员函数。

这样只需要在主程序中调用Rectangle.h, 即可实现全部类的功能。

为什么要这么做?

将类分为规范文件和实现文件提供了很大的灵活性。第一点是因为,如果你将这个代码与其他程序员要共享,不必和这个程序员共享所有的源代码。我可以给他类实现的规范文件和已编译的对象文件即可。然后只需将必要的#include指令插入到他或她的程序中,编译它,并将它与类的对象文件链接起来。这样的话对于代码的隐蔽性较好,不会被其他程序员知道你的源码。

g. 类中的内联函数成员 (Inline function)

在类中,一些比较简单的成员函数,可以写成内联函数,即直接将函数卸载类定义中。

例如:

double getLength() const
{return length;}
//将此定义直接写在类的定义中

但是内联函数必须要相对简单,不能出现循环、条件、选择等复杂的结构。当然,编译系统有可能也会自动将很简单的函数作为内联函数处理,对于复杂的函数忽略内联处理。

内联函数可以干什么?
一言蔽之,内联函数在一定程度上会占用更多的空间,但是能节约CPU的工作占用时间,提高效率。虽然可能有时根本无法分辨速度快慢,但是它可以在一定程度上节约CPU时间。

h. 构造函数(Constructors)

A constructor is a member function that is automatically called when a class object is created

构造函数是在创建类对象时自动调用的成员函数。还是通俗的理解,像是房子(类)刚建好就送过来的家具一样,会自动的去装修这个房子。一旦这个类被实例化,或者在内存中创造了空间,这个程序即会被执行,像是”初始化一样“。它的名字和类名相同,没有返回值,不需要用户显式调用(用户也不能调用)。与之对应的是解构函数(Destructors),这个在下一章提到。

例如,为一个类定义一个简单构造函数:

class Demo
{public:Demo();  // 构造函数,和类的名字一模一样Demo(string, string); //重载构造函数,可以传递两个参数
};Demo::Demo()
{cout << "Welcome to the constructor!\n";
}

因此,当实例化一个Demo类之后,会自动输出 “Welcome to the constructor!”。

Demo box;          //构造函数会执行
Demo box("A","B"); //构造函数会执行,且使用两个传入的两个string类

但是注意的是

Rectangle ptr = nullptr; //这个不会执行构造函数,因为没有实例被创造出来,只是定义了一个空指针。
ptr = new Recangle;      //这个语句才会执行构造函数,因为Rectangle类被实例化了。

通过构造函数,我们能完成很多东西,例如对于类中private的变量进行初始化值等等。

i. 解构函数(Destructors)

A destructor is a member function that is automatically called when an object is destroyed.

和构造函数相反,解构函数是在这个类被撤销时自动执行的。它的名字和类名相同,没有返回值,不需要用户显式调用(用户也不能调用),只不过前面多了一个~号

对于解构函数的定义:

class Demo
{public:Demo(); // Constructor~Demo(); // 解构函数定义
};
//构造函数
Demo::Demo()
{cout << "Welcome to the constructor!\n";
}
//解构函数
Demo::~Demo()
{cout << "The destructor is now running.\n";
}

解构函数可以在类用不到后及时释放内存。

ptr = new Recangle; //定义了一个指针指向一个Rectangle类
delete ptr;         //销毁一个指针即销毁了当前实例化的类,会执行解构函数

3. 继承(Inheritance)

a. 继承

Inheritance allows a new class to be based on an existing class. The new class inherits all the member variables and functions (except the constructors and destructor) of the class it is based on.

继承就是可以从原先的类中继承其原有的成员函数和变量(除了原先类的构造函数和解构函数->指父类的构造函数不会成为子类的构造函数)。通常称被继承的类叫父类,继承的类叫子类。

为什么需要继承?
继承就像英语中’is a…'一样;就像昆虫分类一样。例如,蛾子是(is a)昆虫,蚱蜢也是(is a)昆虫。那么对于他们而言,有一样的地方(昆虫的基本特征(general characteristic)),也有其自身独特的地方(specific characteristic)。从父类继承子类,可以更加简便的管理代码,不需要在每一个类中定义相同的成员变量和函数。

简单的继承示例如下:

class FinalExam : public GradedActivity
{statements......
}
//FinalExam 是子类,从父类GradedActivity类继承来的,继承的方式是public

但是值得注意的是,对于继承也有很多方式,分别是public,private,protected。

一图流解释:

父类的private成员在子类中均不能访问。

这里有一个新的类型:protected。这个类型是子类的函数可以访问父类的成员函数和变量,但是外部依然不能访问的成员。

对于创建的子类的构造函数和传递参数到继承的父类构造函数,使用下面的语法:

ClassName::ClassName(ParameterList) : BaseClassName(ArgumentList) //定义子类的构造函数
例如:Cube::Cube(double w, double len, double h) : Rectangle(w, len)

当然,子类中也同时可以使用重载产生很多构造函数。

在实例化这个子类时:

Cube Box(double w, double len, double h);
//只需要Cube自身构建函数中的三个变量输入即可,在Cube的构建函数会将变量输入父类构建函数

b. 多继承(Multiple inheritance)

一个子类可以不仅只有一个父类,也可以有很多个父类。

class Cube : public Square, public RectSolid;
//定义一个子类Cube,它继承了Square类和RectSolid类

同样的,定义这个子类的构造函数时,也可以传递参数至父类:

Cube::Cube(int side) : Square(side), RectSolid (side, side, side);
//定义了Cube的构建函数,将对应的变量传入父类的构造函数中

当实例化这个子类时:

Cube box(side)
// 和单继承的实例化方法一致

继承的父类成员函数的函数名重名怎么办?

  1. 子类中再重定义一个函数
  2. 使用"::"来声明是哪个父类的成员函数。
 ClassName.FunctionName子类对象.函数名         //默认调用的是子类自己的同名函数ClassName.BaseClassName::FunctionName子类对象.父类名::函数名 //调用父类的同名函数

c.多重继承(Hierarchies)

多重继承和多继承不是一个概念。多继承是横向的,即一个子类继承多个父类。但是多重继承是纵向的,即一个子类可能继承一个父类,但是这个父类又是从另一个父类继承下来的。因此,会产生阶层(hierarchies)。

4. 重载、覆写与重定义(Overloaded, Overridden, Redefined)

a. 重载(Overloaded)

Two or more functions may have the same name, as long as their
parameter lists are different.

重载是函数名相同,参数列表不同的函数。一般只在类内出现。
使用重载可以使一种函数实现不同参数输入但能实现相同操作。通俗一点来说,一个房子(类)有两个卫生间,它们都可以上厕所,但是一个可以带纸进去,一个里面自带卫生纸。

使用的方法例如:

class Demo
{public:Demo();               //构造函数,和类的名字一模一样Demo(string, string); //重载构造函数,可以传递两个参数
};
//重载的时候函数名是一模一样的,但是可以有不同输入变量。

调用这个成员函数时,会自动根据变量列表识别运行这个成员函数。

b. 重写(Overridden)

重写是出现在继承中的。如果子类的函数名和父类的函数名相同、变量也相同,且父类中对应的函数成员必须是虚(virtual)函数。如果函数名相同,变量不同的话即认定为重载,并不会被重写。

重写会将修改父类的函数,如果子类中重写了父类的函数,则父类的同名成员函数会被覆盖,不可再被调用。

例如:

class Person
{public:virtual void Eat(){cout << "Person Eat food" << endl;}virtual void sleep(){cout << "Person sleep " << endl;}void study(){cout << "We need study" << endl;}
};
class Bob : public Person
{public:void Eat(){cout << "Bob Eat dumpling" << endl;}void sleep(){cout << "Bob sleeps and dreams " << endl;}
};
//这个子类的定义就会将父类的两个成员函数覆盖,从而父类的两个成员函数便不可再被调用

注意:
如果子类没有重写此父类的虚成员函数,则默认使用父类的函数调用(没有被重写virtual的函数还是可以使用)。重写是动态链接(dynamic-binding)。即在编译后,依然可以修改父类的函数,不是在编译时确定的。

c. 重定义(Redefine)

重定义也是出现在继承中的。如果子类的函数名和父类中的成员函数名相同(变量表可以不同),则这时候父类的函数就会被重定义。这时候父类的同名成员函数会被隐藏,但是不会被覆盖。

例如:

class Base
{public:void fun(){cout << "Base::fun()" << endl;}
};
class D :public Base
{public:void fun(int){cout << "D fun(int)" << endl;}
};
//这时候如果实例化一个子类,然后调用fun函数,会运行子类的fun函数,父类的不会被执行

当然,如果还是想要调用父类的同名函数,依然可以通过“::”作用域进行约束。

 d.Base::fun(); //调用父类的fun函数

注意:
重定义是静态链接(static-binding)。也就是说,重定义在编译的时候就已经被确定了,和重写/覆盖不同。因此,如果定义了一个基类的指针,则调用其内部被其他子类重定义的成员函数不会发生变化,也就是说原先的成员函数已经被编译且无法修改。

5. 虚函数和纯虚函数(Virtual function/Pure virtual function)

a. 虚函数(Virtual Function)

刚刚前文提到过重写。虚函数的主要作用就是方便子类重写这类函数。重写的好处在于,父类相当于帮子类构建了一个基本像样的框架,但是又可以让子类有丰富它的空间。好比这个房子(父类)里已经有卧室供人休息,但是继承这个房子的人可以对这个卧室进行装修,让它看起来更加实用、可靠。

定义一个虚函数非常的简单,直接在成员函数前添加一个virtual,即可将此成员函数定义为虚函数。

virtual void getWidth()

在子类中若是函数名且变量都一致的情况下,此函数就会被重写,且父类的成员函数不能再被调用。

b. 纯虚函数(Pure virtual function)

纯虚函数与虚函数不同。虚函数是父类的成员函数可以被子类重写,但是纯虚函数必须要子类去重写。设置纯虚函数的目的在于,编写父类的程序员非常期待另外一位程序员继承此父类的子类去重写和改进自己的类。

使用方法则是在函数名后添加0:

virtual void fun(string) = 0;

在子类中则必须去重写当前函数才能被调用。

6. 抽象基类(Abstract base class)

抽象基类提供了一种概念。简单而言,就是提供了采购的需求清单,比如需要去购物,抽象基类就像是清单,写明了需要买什么东西,但是这个东西什么样子,好不好,贵不贵它都不管。因此抽象基类也不能被直接实例化。含有纯虚函数的类称为抽象基类。只有当里面的纯虚函数全部被子类定义和重写后,才能被实例化。

C++ 语言学习入门--类相关推荐

  1. 【C语言】C语言学习入门方案——极具个人风格以及力求放飞自我的

    任何学习都讲究一个节奏,一个先快后慢的方法.C语言在入门上不必停留太久,因为C语言仅靠入门什么都做不了.熟悉C语言仅靠网络上的入门视频和入门书籍是远远不够的. 先了解基本输入输出 熟悉控制流 了解一些 ...

  2. Erlang语言学习入门

    这是一个命令行程序,可以直接在里面输入表达式进行计算,例如来一个简单的: Erlang R15B01 (erts-5.9.1) [smp:4:4] [async-threads:0] Eshell V ...

  3. vs怎么配置c语言codemac,在Mac上使用vs-code快速上手c语言学习(入门文,老鸟退散)...

    jsf初学解决faces 中文输入乱码问题 中文乱码,貌似在java里很常见,请看简单代码: 很简单的faces win8 中使用第三方无线网卡出现无线连接受限解决办法 无线路由  无线网络模式基本设 ...

  4. C语言学习入门之字符串和转义字符

    活动地址:CSDN21天学习挑战赛 前言 本篇整理的是C语言基础中的字符串和转义字符的知识 一.字符串是什么? 由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Li ...

  5. C语言学习入门篇(1.1)

    最近复习了之前的C语言,也查阅了相关资料,感觉C还是比较容易上手的,因此整理了一些关于C语言的基础知识. (注意!这篇文章适合入手C的小白查阅,如果是大佬还是算了,我也算个小白,不是很懂C,因此如果接 ...

  6. C语言学习入门(一)

    研究生导师要我学单片机,好像需要C语言基础,以前学的全都忘了,二级证书好像白考,所以重新学一下,给自己记个笔记.Fighting! 集成开发环境:MSVC.VS2019etc,这里选择VS2019 创 ...

  7. Java快速入门学习笔记7 | Java语言中的类与对象

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  8. Python语言学习:python语言的特点、入门、基础用法之详细攻略

    Python语言学习:python语言的特点.入门.基础用法之详细攻略 相关内容 Python 基础教程 目录 python语言的特点 python语言的入门 python语言的基础用法 python ...

  9. 【Python】Python语言学习:面向对象编程,类和对象,封装、继承和多态

    这一周Python语言学习,记录如下. 01 面向对象编OOP 1.1 为什么学习和应用OOP? 1 OOP适合更加复杂的需求分析和项目开发. 2 OOP具有更强大的封装能力. 3 OOP相比于面向过 ...

  10. 零基础学习C语言如何入门(内附工具书推荐+视频教程)

    C语言同C++.Python等都是通用性的编程语言,它们不针对某一个特定的方向,能做的事情很多.C语言主要用于底层开发,Windows.Linux.Unix 等操作系统的内核90%以上都使用C语言开发 ...

最新文章

  1. 数据中心(机房)施工方案
  2. 暑期应用开发怎么玩?申请蓝牙Mesh网关操控家里的一切
  3. 笨办法学 Python · 续 练习 52:`moreweb`
  4. C语言和设计模式(备忘录模式)
  5. 斐波那契数列:一道100年后羊圈羊的数量算法题
  6. 分布式系统「伸缩性」大招之——「弹性架构」详解
  7. 《2008胡润百富榜》前三名
  8. 信息的哲学--从信息到数据存储,再到数据保护
  9. 史上最详细mac安装Qt教程
  10. 琐事记 - 2015/10/28
  11. Android 读取本地Word/Pdf/Txt文件转文本输出
  12. Python警告控制模块:warnings
  13. JMS介绍:我对JMS的理解和认识
  14. skiplist - 跳表
  15. 商务智能 BI 的四大关键技术
  16. 等保(网络安全等级保护)2.0与定级备案之——等保2.0与等保1.0区别解读
  17. 解决win7系统chrome证书错误问题,证书无效,不受信任的网站问题 - windows没有足够信息不能验证该证书
  18. docker制作nginx+nginx-module-vts镜像基于alpine镜像
  19. 关于HTML5语音Web Speech
  20. 前脚我的 GPT4 被封,后脚收到了文心一言的邀请账号

热门文章

  1. 验证二叉树的前序序列化[抽象前序遍历]
  2. EXCEL表格复制到空白的EXCEL表格变形 解决方法
  3. linux下搭建测试环境
  4. 懂生意的产品经理,才能做好商业化
  5. word文件怎么压缩?详细的操作步骤
  6. 坚果nuts 加速 官网_坚果 R2 发布:骁龙 865、1 亿像素、双曲面屏,售价 4499 元...
  7. Invisible Backdoor Attack with Sample-Specific Triggers
  8. 网上赚钱靠谱的方法,看懂了的都是老手!
  9. 如何解决电脑网络提示无Internet访问权限
  10. try固定搭配_英语词汇:regret cease try等词的固定搭配用法