C++ 对象

·351 字·2 分钟
圆润的海豚
作者
圆润的海豚
海阔凭鱼跃

1.类 #

在了解类之前,我们需要首先了解一下什么是面向对象。

1.1面向对象的三大特征 #

(来源于百度百科)

  • 封装性:封装使数据和加工该数据的方法(函数)封装为一个整体,以实现独立性很强的模块,使得用户只能见到对象的外特性(对象能接受哪些消息,具有那些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象者的使用分开,使用者不必知晓行为实现的细节,只须用设计者提供的消息来访问该对象。
  • 继承性:继承性是子类自动共享父类之间数据和方法的机制。它由类的派生功能体现。一个类直接继承其它类的全部描述,同时可修改和扩充。继承具有传递性。继承分为单继承(一个子类只有一父类)和多重继承(一个类有多个父类)。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力。
  • 多态性:所谓多态就是指对象根据所接收的消息而做出动作,同一消息为不同的对象接受时可产生完全不同的行动,这种现象称为多态性。多态机制使具有不同内部结构的对象可以共享相同的外部接口。

1.2类的访问权限 #

在C++中,我们使用访问说明符加强类的封装性,一个类可以包含0个或多个访问说明符。

  • 定义在public说明符之后的成员在整个程序内可被访问,public成员定义类的接口。
  • 定义在private说明符之后的成员可以被该类的中的函数以及友元函数访问,但是不能被使用该类的代码访问,private部分封装了类的实现细节。
  • 定义在protected说明符之后的成员可以被该类中的函数子类的函数、以及其友元函数访问,但不能被该类的对象访问。

2.vector #

标准库类型vector(容器)表示对象的集合,其中需要所有的对象类型都相同。集合中的每个对象都有一个与之对应的索引,用于访问对象。 vector是一个类模板,编译器根据模板创建类或函数的过程为实例化,使用模板时,需要指出编译器应把类或函数实例化成何种类型,即在模板名字后跟一对尖括号,在括号中放上信息。

vector<int> preorderTraversal表示,在pre中保存int类型的对象。

2.1 定义和初始化vector对象 #

方法 含义
vector<T> v1 v1是一个空容器,类型为T
vector<T> v2(v1) v2中包含有v1所有元素的副本
vector<T> v2 = v1 等价于v2(v1)
vector<T> v3(n, val) v3包含了n个val
vector<T> v4(n) v4包含了n个初始化T的对象
vector<T> v5{a, b, c…} v5包含了初始个数的元素,每个元素被赋予相应的值
vector<T> v5 = {a, b, c…} 等价于v5{a, b, c…}

2.2 向vector对象中添加元素 #

vector的成员函数push_back能够把一个值当成vector对象的尾元素push到vector对象的尾端。 例如ret.push_back(root->val); 注:如果在循环体内部包含有向vector对象添加元素的语句,不能使用范围for循环。

2.3 其它vector操作 #

操作 含义
v.empty() 如果v中不含有任何元素,返回真,否则返回假
v.size() 返回v中元素的个数
v.push_back(t) 向v的尾端添加一个值为t的元素
v[n] 返回v中第n个位置上元素的引用
v1 = v2 用v2中元素的拷贝替换v1中的元素
v1 = {a, b, c…} 用列表中元素的拷贝替换v1中的元素
v1 == v2 判断v1和v2中的元素数量与对应元素值都相等
v1 != v2
<, <=, >, >= 以字典顺序进行比较

注:不能用下标形式添加元素。

2.4 迭代器 #

所有标准库容器都可以使用迭代器,它的对象是容器中的元素或string对象中的字符,有迭代器的类型同时拥有返回迭代器的成员,使用迭代器可以访问某个元素或对其进行操作,迭代器和指针类似,有有效与无效之分。 其中, begin成员返回指向容器第一个元素的迭代器; end成员返回指向容器尾元素的下一位置的迭代器; 例如:

auto b = v.begin(), e = v.end();

b表示v的首元素,e表示v尾元素的下一位置,它们的类型与begin和end的返回值类型相同

标准容器迭代器的运算符:

运算符 含义
*iter 返回迭代器所指元素的引用
iter->mem 解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem
++iter 令iter指向容器中的下一个元素
–iter 令iter指向容器中的上一个元素
iter1 == iter2 判断两个迭代器是否相等,如果两个迭代器指向同一元素或是同一个容器的尾后迭代器则相等
iter1 != iter2

注:凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。

vector和string提供了更多的运算符:

运算符 含义
iter + n 迭代器指向的位置向前移动n个位置
iter - n 迭代器指向的位置向后移动n个位置
iter += n 将iter加n的结果赋给iter
iter -= n 将iter减n的结果赋给iter
iter1 - iter2 计算两个迭代器之间的距离
>、>=、<、<= 关系运算, 比较两迭代器指向元素位置的前后关系

1.顺序容器 #

1.1 顺序容器类型 #

类型详情见最上方思维导图,在此不过多赘述。

  1. string和vector将元素保存在连续的内存空间中,能够使用下标进行快速的随机访问,但是在中间添加或删除元素非常耗时。
  2. list和forward_list与上述互补,即令任何位置的添加和删除操作变得快速,但是不支持元素的随机访问。且forward_list没有size操作。
  3. deque与vector类似,但是它在两端的添加或删除操作很快。
  4. array的大小是固定的,不支持添加和删除操作。

1.2 容器操作 #

常见操作详见 DAY1,下面进行一些补充

成员 含义
c.back() 返回c中尾元素的引用
c.front() 返回c中首元素的引用
a.swap(b) 等价于 swap(a, b) 交换a和b的元素
c.emplace(inits) 使用inits构造c中的一个元素
c.erase(args) 删除args指定的元素
c.clear() 删除c中的所有元素,返回void
c.pop_back() 删除c中尾元素 c++11
c.pop_front() 删除c中首元素 c++11
c.erase(p) 删除迭代器p所指定的元素,返回一个指向被删元素之后元素的迭代器
c.erase(b, e) 删除迭代器b和e范围内的元素,返回一个指向最后一个被删元素之后元素的迭代器
插入操作 含义
c.push_front(t) 在c的头部创建一个值为t或由args创建的元素
c.emplace_front(args)
c.insert(args) 将args中的元素拷贝进c
c.insert(p, t) 在迭代器p指向的元素之前创建一个值为t或由args创建的元素,返回指向新添加元素的迭代器
c.emplace(args)
c.insert(p, n, t) 在迭代器p指向的元素之前插入n个值为t的元素,返回指向新添加的第一个元素的迭代器,若范围为空,则返回p
c.insert(p, b, e) 将迭代器b和e指定的范围内的元素插入到迭代器p指向的元素之前,b和e不能指向c中的元素,返回指向新添加的第一个元素的迭代器,若范围为空,则返回p
c.insert(p, il) il是一个花括号包围的元素值列表,将这些定值插入到迭代器p指向的元素之前,返回指向新添加的第一个元素的迭代器,若范围为空,则返回p
注:
  • 所有容器都支持相等或不等运算符;
  • vector和string不支持push_front和emplace_front
  • forward_list不支持push_back和emplace_back
  • array不支持这些操作
  • 向一个vector、string和deque插入元素会使所有指向容器的迭代器、引用和指针失效。

迭代器:详见 DAY2,其中,forward_list迭代器不支持递减运算符,并且DAY1中所列举的算术运算只能应用于string、vector、deque和array的迭代器。 迭代器范围:由一对迭代器表示,通常称为begin和end,迭代器范围中的元素包含begin所表示的元素以及begin和end之间所有的元素(end指向尾元素之后的位置),这种元素范围称为左闭合区间。 因此可以用如下的代码用循环来处理一个元素范围:

while(begin != end){
	*begin = val;
	++begin;
}

关于list和forward_list的操作,可以看这篇博客 https://blog.csdn.net/u013006553/article/details/78158717

1.3 容量 #

操作 含义
c.resize(n) 调整c的大小为n个元素
c.resize(n, t) 调整c的大小为n个元素, 任何新添加的元素都初始化为值t
c.capacity() 不重新分配内存空间的话,c可以保存多少元素
c.reserve(n) 分配至少能容纳n个元素的内存空间

2.容器适配器 #

除了顺序容器,标准库还定义了三个顺序容器适配器:栈适配器stack、队列适配器queue和priority_queue。

栈适配器新操作 含义
s.pop() 删除栈顶元素
s.push(item) 压栈
s.top() 返回栈顶元素
队列适配器新操作 含义
q.pop() 返回queue的首元素或priority_queue的最高优先级的元素
q.front() 返回首元素或尾元素
q.back() 只适用于queue
q.top() 返回最高优先级元素,只适用于priority_queue
q.push(item) 在queue末尾或priority_queue中恰当的位置创建一个元素

3.关联容器 #

联容器中的元素是根据关键字储存的,支持普通容器操作,但是不支持顺序容器位置相关的操作。 具体内容将在DAY4中更新。

4.intersect()求交集 #

c=intersect(a,b)找出向量a与b的相同元素,并按升序返回到c中。

3. 其他 #

3.1 nullptr #

在c语言中,NULL实际上是一个空指针,在使用中被隐式转换成其它类型; 而c++是强类型语言,不能进行隐式类型转换,NULL直接定义为0,定义nullptr为空指针。

3.2 输入输出 #

在初入C++时,会发现和C的头文件引用有了区别,我们用了#include<iostream> iostream库包含两个基础类型istreamostream,分别表示输入流输出流,流的意思是字符是顺序生成或消耗的。 标准库定义了四个IO对象:

  • 输入,使用名为cin的istream类型的对象;
  • 输出,使用名为cout的ostream类型的对象;
  • 输出警告和错误信息,使用名为cerr的ostream类型的对象;
  • 输出程序运行时的一般性信息,使用名为clog的ostream类型的对象。

1.结合输出运算符«,可以完成以下操作:

std::cout << "hello world" << std::endl;

第一个«运算符将右侧给定的值(“hello world”)写到左侧给定的ostream对象(cout)中,下一个«运算符又将endl写到左侧的新对象中。其中endl是一个被称为操纵符的特殊值,写入它的效果是结束当前行并将缓冲区中的内容刷到设备中。

2.结合输入运算符»,可以完成以下操作:

int v1 =0, v2 = 0;
std::cin >> v1 >> v2;

同理,»从左侧给定的istream(cin)读入数据,并存给右侧的给定对象(v1),返回左侧的对象作为计算结果,第二个»又重复了一遍上述操作,即从cin中读入两个值,分别存入v1和v2中。

3.3 命名空间namespace #

我们可以发现程序中使用std::cout而不是cout,因为前缀std::指出名字cout是定义在名为std命名空间中的。命名空间可以帮助我们避免名字定义冲突,以及使用库中相同名字导致的冲突,标准库定义的所有名字都在std中。 其中::是作用域运算符,它指出我们想使用定义在命名空间std中的名字cout。

每次都使用std::cout太过于麻烦,我们可以对命名空间进行using声明,声明后就无需前缀也能使用所需的名字了。 using声明的格式:using namespace::name; 例如,

#include<iostream>
using std::cin;
using std::cout;
using std::endl;

于是,我们就可以愉快地直接使用cin, cout, endl了。

注:头文件中不应包含using声明。