C++ 术语与概念

C++ 是在C语言基础上开发的,一种面向对象编程的语言。C++在C语言基础上封装了类实现了面向对象的功能,封装了函数实现了泛型编程的功能。与C相比具有安全性更高、功能更强的特点。本文以wiki知识库的形式,介绍C++的基本概念和语法规则,并在每个术语旁边配有对应的英文,为初学C++的人提供一份快速查阅的手册。旨在用最简短的文字,介绍清概念以及用法。

基本概念

面向对象编程 Object Oriented Programming(OOP)

编译过程:编译+连接

计算机语言的分类

  • 机器语言:二进制代码
  • 汇编语言:机器语言的简化,直接对地址、内存、寄存器进行操作,汇编代码与机器语言是一一对应的
  • 高级语言:抽象程度更高,需要编译成机器语言
  • 脚本语言:不需要编译

分类

  • 关键字
  • 标识符

基本数据类型与运算

数据类型

  • 基本数据类型:整型int,浮点型float,双精度浮点double,字符char,布尔bool
  • 自定义数据类型:枚举类型,结构体类型,联合体类型,类类型

流程控制

  • 条件判断:if,while,switch

C语言和C++中,所有非零的值都被判定为逻辑真,为零的值才会被判定为逻辑假。

if、while语句中的逻辑判断是分步进行的,比如与运算中,只要前一项为非,不会对后一项进行判断,而是直接跳出判断。

数据的存储与操作

每个数据都会单独分配内存空间来处理

CPU只是对内存数据进行操作,而不会区分不同的数据类型。区分数据类型并选择不同的操作,是编译器需要完成的工作。

函数 Function

函数由参数表和函数体组成,函数在使用前必须先声明,可以之后再定义

参数传递

函数在调用时才分配存储单元

值传递

语法规定:传递给函数的实参,与函数形参表中的数据类型一致

//函数声明,此处省略函数定义
void swap(int a, int b);

int main(){
    int a, b;
    swap(a, b);    //函数引用,因为传递的是值,此处a, b没有被交换

    return 0;
}

引用传递

语法规定:使用“&”符号定义一个引用,引用必须指向已经存在的对象。函数的形参是一个引用,传递给函数的实参是被引用对象

int i;
int& ri = i;    //ri是i的一个引用

函数实例:

//函数声明,此处省略函数定义
void swap(int& a, int& b);

int main(){
    int a, b;
    swap(a ,b);    //函数引用,因为传递的是引用,此处a, b被交换

    return 0;
}

指针传递

语法定义:传递实参的地址给子函数,子函数的形参表中定义形参为指针类型

//函数声明,此处省略函数定义
void swap(int* a, int* b);

int main(){
    int a, b;
    swap(&a, &b);    //函数引用,传递指针给子函数
    return 0;
}

函数的调用

运行栈

最后调用的函数总是最先返回,因此可以用栈这种数据结构来保存。运行栈就是专为函数调用设计的数据结构,

指针

寄存器中通常会保存两个指针,分别指向栈顶和函数调用发生的位置,来进行函数的调用和返回。

  • 栈顶指针:指向栈顶的地址
  • 帧指针:指向函数调用时的地址

函数的声明与安全性

C++ 要求函数在使用时先声明返回值类型和参数表,这样可以在编译过程中发现错误,是设计更为合理的编程语言。如果不做声明,在参数传递和参数返回两个过程中,都可能发生错误。

  • 参数传递中,有可能把参数类型传错,将整型变量传递给浮点变量,而不做类型转换。
  • 参数返回中,不做声明,有可能用整型方法去获取void类型的函数值,就会读取垃圾数据。

类与对象 Class & Object

构造函数与析构函数 Constructor and Destructor

构造函数

语法定义:函数名没有返回值类型,函数名和类名一致,不允许有返回值列席,不允许有return语句。当没有声明构造函数时,编译器会自动生成默认的构造函数,用于分配类成员的空间。

//类定义
class Point{
	double x,y;
public:
	Point(){};	//构造函数声明
}

初始化列表

除参数表和函数体以外,构造还可以拥有初始化列表,效率比在函数体中赋值要高一些。因为当传递参数是类时,在初始化列表中进行初始化,只需要执行一次复制构造函数。若在函数体中进行初始化,首先需要调用构造函数构造对象,再调用构造函数的赋值运算符进行赋值运算。

语法定义:初始化列表位于参数表和函数体之间,用冒号跟参数表隔开,初始化对象之间用逗号分隔。

//类定义
class Point{
	double x,y;
public:
	Point(double init_x, double init_y):x(init_x),y(init_y){}	//含初始化列表的构造函数
}

委托构造函数

使用委托构造函数,可以保证代码的一致性,提高代码的复用率,降低修改成本

语法定义:定义构造函数时,可以在初始化列表中使用其他已经声明的构造函数

//类定义
class Point{
	double x,y;
public:
	Point(double init_x, double init_y):x(init_x),y(init_y){}	//含初始化列表的构造函数
	Point():Point(0.0,0.0){}	//使用委托构造函数来实现默认构造函数
}

复制构造函数

语法定义:函数名与类名一致,形参表为const定义的类对象的引用。可以用"=delete"指令隐藏复制构造函数。有两个要点:复制和构造。

//类定义
class Point{
	double x,y;
public:
	Point(double init_x, double init_y):x(init_x),y(init_y){}	//含初始化列表的构造函数
	Point():Point(0.0,0.0){}	//委托构造函数
	Point(const Point &p){}	//复制构造函数
}

//构造函数在主程序中的使用
int main(){
	Point a;	//默认构造函数被调用
	Point b(0.0, 0.0);		//含参数表的构造函数被调用
	Point c(a);		//复制构造函数被调用
}

析构函数

语法定义:函数名在类名前加"~",不允许有参数表,不允许有返回值列席,不允许有return语句。不定义时,编译器会自动生成析构函数。在对象消亡时,析构函数会被自动调用。

//类定义
class Point{
	double x,y;
public:
	Point(double init_x, double init_y):x(init_x),y(init_y){}	//含初始化列表的构造函数
	Point():Point(0.0,0.0){}	//委托构造函数
	Point(const Point &p){}		//复制构造函数
	~Point(){}		//析构函数
}

组合类

  • 组合类的构造函数:初始化的顺序与类中声名的顺序相同,与初始化列表中的顺序不同,组合类初始化时,会调用两次初始化构造函数,第一次是将实参赋值给形参,第二次是把形参值赋值给组合类中的对象。

  • 前向引用声名:只声名类的名字而不包含任何细节。

UML

  • 事物:
  • 关系:依赖,重数,聚集(组合),泛化
  • 图:

特殊类

  • 结构体:在C++中是特殊的类,成员默认是public
  • 联合体:成员共用存储空间,只有一个成员有效,可以使用无名的联合体。
  • 枚举类:类型控制更严格,无法比较不同类的枚举类型;作用域限制在类中,可以使用同样的名字。

数据的共享与保护

作用域

作用域由小到大可以分为以下几种:

  • 函数原型作用域:只在形参表中存在
  • 局部作用域:又称为块作用域,变量定义所在的最小的一对大括号内
  • 类作用域:
  • 文件作用域:又称为静态作用域
  • 命名空间作用域:

可见性

作用域和可见性可能不一致:当内层作用域与外层作用域出现变量重名

生存期

  • 静态生存期:文件作用域中定义的变量,或用static声明的变量;如果在函数体内声明静态变量,那么该变量只会初始化一次,在之后对函数的调用过程中,访问的是同一个变量。
  • 动态生存期:与作用域一致,同时消亡;在函数中调用的会随着函数运行栈的消亡而消亡

类的静态对象

  • 为所有类的对象共有,可以使用类名直接调用
  • 静态数据:声明在类体重,初始化和定义必须在类体之外
  • 静态函数:无法确定调用对象,不可直接访问对象的非静态变量

友元: freind

  • 友元函数:在类中声明,在类外定义,需要把对象当做实参传递给函数
  • 友元类:友元关系式单向的

常类型:const

  • 常对象:必须初始化,不能被更新

  • 常成员:

    a. 常数据成员

    b. 常函数成员(一种函数重载的判断条件):承诺不改变数据状态,并由编译器进行检验(bitwise检验)
    注一:可使用“mutable” 摆动场,释放非静态成员的bitwise constness 约束,从而使得常函数可以修改这些非静态成员的值。
    注二:可使用转型“casting”方法,用non-const函数调用const函数,从而实现代码复用。

  • 常引用:提高参数传递效率的同时,满足安全性的条件

  • 常指针

const int* p1;    //指向整型常量的指针
int const * p2;   //指向整型常量的指针
int* const p3;    //指向整型变量的常量指针

多文件结构

C++工程结构:声明文件、定义文件、使用文件

  • 外部变量:文件作用域中定义的变量,默认都是外部变量。使用时需要用extern关键字声明之后,才可以使用
  • 外部函数:
  • 编译预处理:#include, #define, #if #endif #else #elif, #ifdef

标准C++库

  • 输入输出类iostream:
  • 容器类与抽象数据类型stl:
  • 存储管理类
  • 算法algorithms:
  • 错误处理
  • 运行环境支持

指针和数组 Pointers & Array

&: 取数据对应的地址,*: 取地址对应的数据

指针的应用场景

  • 动态内存分配,返回值只能是指针
  • 深拷贝:自定义构造函数,为类内的指针属性申请内存空间。

指针 Pointers

  • 定义:用于存放地址类型的变量
  • 初始化:指针变量的赋值必须是合法获得的地址,不可用非静态变量去初始化静态指针,空指针:nullptr
  • 指向常量的指针,指针类型的常量
  • 指针的算术运算:+n指向第n个数据的起始位置
  • 指针的关系运算:指向相同类型的指针可以进行关系运算,也可以跟0进行关系运算

数组 Array

普通数组

语法定义:类型说明符 + 数组名 + [常量表达式][常量表达式]

int array_1[3];		//一维数组
int array_2[3][4];	//二维数组

注意数组名后接的一定是常量表达式,不可以是变量

  • 初始化:可以只给一部分元素初始值,数组名保存首元素的内存地址,为常量
int array_1[10] = {0,1,2,3,4,5,6,7,8,9};	//全部初始化
int array_2[10] = {0,1,2,3};	//部分初始化,未初始化部分为0
int array_3[] = {0,1,2,3,4,5,6,7,8,9};	//全部初始化可以不声明数组长度
int array_4[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};	//二维数组初始化可以用大括号隔开 
  • 传递:数组作为函数参数传递时,直接传递指针

指针数组

语法定义:类型说明符 + 指针运算符 + 数组名 + [常量表达式][常量表达式]

代码实例

int *pa[3];

指针数组的用法跟多维数组类似,但是多维数组可以按照一维数组来使用,而指针数组不行。

函数与指针

指针类型的函数

语法定义:存储类型 + 数据类型 + *函数名(形参表){函数体}

代码实例

int* func_1(){}

注意:返回值必须是在主调函数中合法有效的地址,合法的地址包括

  • 数组作为参数传递给函数
  • 子函数中动态分配的地址

指向函数的指针

语法定义:存储类型 + 数据类型 + (*函数名)(形参表){函数体}

典型用途:函数回调

代码实例

//定义主调函数
int compute(int a, int b, int(*func)(int, int))
{return func(a,b);}
//定义求最值的函数
int max(int a, int b)
{return ((a>b)?a:b);}
//定义求和函数
int sum(int a, int b)
{return a + b;}

int main(){
	int a, b;
	compute(a, b, &max);	//求最大值
	compute(a, b, &sum);	//求和
}

对象与指针

  • 使用语法:对象指针 + “->” + 成员
Point p;
Point* ptr;		//定义对象指针
ptr = &p;
p.getx();		//使用对象调用成员函数
ptr->getx();	//使用对象指针调用成员函数
  • this指针:类对象隐含的指针

动态内存分配

  • 使用语法:new 类型名T(初始化参数列表);delete 指针标识符;delet[] 数组标识符
Point *ptr = new Point(0.0,0.0);
delete ptr;		//删除对象,但不删除指针
ptr = new Point(1, 2);
delete ptr;
  • 注意事项:分配和释放一定要配合使用

智能指针

  • unique_ptr :不允许多个指针共享资源,可以用标准库中的move函数转移指针
  • shared_ptr :多个指针共享资源
  • weak_ptr :可复制shared_ptr,但其构造或者释放对资源不产生影响

对象的复制与移动

复制

  • 浅层复制:数据成员的一一对应
  • 深层复制:在复制对象数据成员的基础上,复制指针指向的动态内存空间

移动构造

语法定义:函数名与类名一致,参数表为类对象的右值引用

代码实例

class IntNum{
public:
	IntNum(IntNum && n): xptr(n.xptr){
		n.xptr = nullptr;		//将原始值赋值为nullptr
	}

字符串

  • C风格字符串:字符串数组,最后一位用"\0"结尾
  • C++风格:string 类

继承 Inherit

基本概念和语法

语法定义: class + 派生类名 + : + 继承方式 + 基类名

class DerivedPublic : public Base {}         //基类Base的派生类(公有继承)
class DerivedPrivate : private Base {}       //基类Base的派生类(私有继承)
class DerivedProtected : protected Base {}   //基类Base的派生类(保护继承)

继承方式

类成员的三种访问权限与三种继承方式是对应的,分别限定了类成员对于类对象、子类成员和子类对象的可见性。如果需要基类的某个成员对其派生类可见,但是对其对象不可见,那么就采用保护的可见性。

公有继承

公有继承不改变基类成员的可见性

成员类型基类对象派生类成员派生类对象
public可见可见可见
protected不可见可见不可见
private不可见不可见不可见

私有继承

私有继承将基类成员全部转变为派生类的私有成员

成员类型基类对象派生类成员派生类对象
public可见可见不可见
protected不可见可见不可见
private不可见不可见不可见

保护继承

保护继承将基类的共有成员转变为保护成员,其他成员的可见性不变

成员类型基类对象派生类成员派生类对象
public可见可见不可见
protected不可见可见不可见
private不可见不可见不可见

类型转换

派生类对象可以转换为基类对象。

注意:不要定义继承来的非虚函数

派生类的构造和析构函数

构造函数

语法定义:派生类名::派生类名(基类所需的形参,本类成员所需的形参):
基类名(参数表), 本类成员初始化列表

可以用using直接使用基类的构造函数。

代码示例

class B;	//基类B
class C: public B{	//派生类C
public:
	C(int i, int j);
}
//派生类的构造函数
C::C(int i,int j): B(i), c(j){
}

复制构造函数

语法定义:派生类名::派生类名(const 派生类名 &派生对象):
基类名(派生对象)

原理在于派生对象可以直接转换为基类对象

程序实例

C::C(const &c1):B(c1){}

析构函数

语法定义:派生类名::~派生类名()

并不需要显式地调用基类的析构函数

程序实例

C::~C(){}

访问基类的成员

二义性

当同名成员被多次定义的时候,出现了二义性,可以使用类名来限定。

当派生类从多个基类派生,而这些基类又有共同基类时,二义性会导致程序的冗余,甚至引起混淆或错误。

虚基类

慎用虚基类

语法定义:使用virtual调用

代码实例

class C: virtual public B{}

构造函数:最远派生类(建立对象时使用的类)给虚基类传递函数。

多态性 Polymorphism

  • 编译多态性:重载运算符, 重载函数
  • 运行多态性:虚函数

运算符重载

重载规则

运算符的重载不改变运算符的优先级,并且只能重载C++中已经包含的运算符

不能够重载的运算符: “.”, “.*”, “::”, “?:”

双目运算符重载为成员函数

语法定义:函数类型 operator 运算符(形参)

参数个数 = 原操作数 - 1

代码实例

class Complex{
public:
	Complex operator+(const Complex &c2)const {
		return Complex(real+c2.real, img+c2.img);
	}
}

单目运算符重载为成员函数

语法定义:

  • 前置:函数类型 &operator 运算符()

  • 后置:函数类型 operator 运算符(int)

代码实例

Clock& operator ++ ();	//前置运算符
Clock operator ++ (0);	//后置运算符

重载操作符为非成员函数

语法定义:需要列出所有的操作数,前置、后置单目运算符需要添加一个int

代码实例

Complex operator+(const Complex &c1, const Complex &c2){
    return Complex(c1.real+c2.real, c1.imag+c2.imag); 
}
Complex operator-(const Complex &c1, const Complex &c2){
    return Complex(c1.real-c2.real, c1.imag-c2.imag); 
}

ostream & operator<<(ostream &out, const Complex &c){
    out << "(" << c.real << ", " << c.imag << ")";
    return out;
}

虚函数

虚函数

语法定义:virtual 函数类型 函数名(形参表)

虚函数不可以是静态,构造函数不可以是虚函数

代码实例

vitual void display() const;

虚析构函数

语法定义:与虚函数的定义一样

使用虚析构函数,可以保证动态申请的内存空间能够被释放。否则,基类指针指向的派生类对象消亡时,只会调用基类的析构函数。

虚表与动态绑定

每个多态类有一个虚表,虚表中有当前各个虚函数的入口地址

每个对象有一个指向当前虚表的指针

抽象类

纯虚函数: virtual 函数类型 函数名(参数表) = 0

包含纯虚函数的类为抽象类,不能够构造对象

显示函数覆盖 overriide

不允许被继承 final

语法定义: class 对象名 final;

clase Base1 final {}

模板 Template

模板

函数模板

语法定义:template<模板参数表> 函数定义
模板参数表:typename 或者 class

template<typename T>
T abs(T x){
	return x < 0? -x:x;
}

类模板

语法定义:template<模板参数表> 类定义

template<class T>
class Test{
private:
	T x;
public:
	Test(T n);
}

线性群体

直接访问

数组

数组类模板特点

  • 存储空间在内存上连续,可以直接访问数组成员,访问开销为O(1)

数组基本功能

  • 构造函数:构造函数、复制构造函数、析构函数
  • 重载操作符:[], *, =
  • 数组操作:修改数组大小、取数组大小

代码实现

template <class T>  //数组类模板定义
class Array {
private:
	T * list;        //用于存放动态分配的数组内存首地址
	int size;       //数组大小(元素个数)
public:
	Array(int sz = 50);     //构造函数
	Array(const Array<T> &a);   //复制构造函数
	~Array();           //析构函数
	Array<T> & operator = (const Array<T> &rhs);    //重载"=",赋值运算符的返回值为数组的引用
	T & operator [] (int i); //重载"[]"
	const T & operator [] (int i) const;     //重载"[]"常函数
	operator T * ();        //重载到T*类型的转换,指针函数不要求声明返回值类型
	operator const T * () const;
	int getSize() const;        //取数组的大小
	void resize(int sz);        //修改数组的大小
}

顺序访问

节点 Node

节点是顺序访问的基本元素

节点特点

节点基本功能

代码实现

template <class T>
class Node {
private:
	Node<T> *next;  //指向后继结点的指针
public:
	T data; //数据域
	Node(const T &data, Node<T> *next = 0);    //构造函数
	void insertAfter(Node<T> *p);   //在本结点之后插入一个同类结点p 
	Node<T> *deleteAfter(); //删除本结点的后继结点,并返回其地址
	Node<T> *nextNode();            //获取后继结点的地址
	const Node<T> *nextNode() const;     //获取后继结点的地址
};

链表 List

链表特点

  • 访问必须从第一个对象开始

链表基本功能

  • 构造函数:构造函数、复制构造函数、析构函数
  • 操作符重载:

代码实现

template <class T>
class LinkedList {
private:
	//数据成员:
	Node<T> *front, *rear;  //表头和表尾指针
	Node<T> *prevPtr, *currPtr;   //记录表当前遍历位置的指针,由插入和删除操作更新
	int size;   //表中的元素个数
	int position;   //当前元素在表中的位置序号。由函数reset使用

					//函数成员:
					//生成新结点,数据域为item,指针域为ptrNext
	Node<T> *newNode(const T &item, Node<T> *ptrNext = NULL);

	//释放结点
	void freeNode(Node<T> *p);

	//将链表L 拷贝到当前表(假设当前表为空)。
	//被拷贝构造函数、operator = 调用
	void copy(const LinkedList<T>& L);

public:
	LinkedList();   //构造函数
	LinkedList(const LinkedList<T> &L);  //拷贝构造函数
	~LinkedList();  //析构函数
	LinkedList<T> & operator = (const LinkedList<T> &L); //重载赋值运算符

	int getSize() const;    //返回链表中元素个数
	bool isEmpty() const;   //链表是否为空

	void reset(int pos = 0);//初始化游标的位置
	void next();    //使游标移动到下一个结点
	bool endOfList() const; //游标是否到了链尾
	int currentPosition() const;    //返回游标当前的位置

	void insertFront(const T &item);    //在表头插入结点
	void insertRear(const T &item);     //在表尾添加结点
	void insertAt(const T &item);       //在当前结点之前插入结点
	void insertAfter(const T &item);    //在当前结点之后插入结点

	T deleteFront();    //删除头结点
	void deleteCurrent();   //删除当前结点

	T& data();              //返回对当前结点成员数据的引用
	const T& data() const;   //返回对当前结点成员数据的常引用

							 //清空链表:释放所有结点的内存空间。被析构函数、operator= 调用
	void clear();
}

栈 Stack

栈特点

  • 只能从一端存储和读取数据

栈基本功能

  • 构造函数:
  • 栈操作函数

代码实现

template <class T, int SIZE = 50>
class Stack {
private:
	T list[SIZE];
	int top;
public:
	Stack();
	void push(const T &item);
	T pop();
	void clear();
	const T &peek() const;
	bool isEmpty() const;
	bool isFull() const;
}

队列 Quene

队列特点:

  • 只能从有一段存储数据,从另一端删除数据

队列基本功能:

  • 构造函数:
  • 队列操作(有修改的):入队、出队、清空
  • 队列操作(无修改的):访问队首元素、求队列长度、判断队列是否为空、判断队列是否满

代码实例

template <class T, int SIZE = 50>
class Queue {
private:
	int front, rear, count; //队头指针、队尾指针、元素个数
	T list[SIZE];   //队列元素数组
public:
	Queue();          //构造函数,初始化队头指针、队尾指针、元素个数
	void insert(const T &item); //新元素入队
	T remove(); //元素出队
	void clear();   //清空队列
	const T &getFront() const;  //访问队首元素
								//测试队列状态
	int getLength() const;//求队列长度
	bool isEmpty() const;//判断队列空否
	bool isFull() const;//判断队列满否
}

索引访问

排序

排序分为内排序、外排序。内循环全部在内存中进行循环,外排序需要分批次从硬盘中读取数据。

插入排序

每次将待排序元素与已排序数组进行比较,并插入到合适位置上去。

选择排序

在未排序队列中选择最小的数据,放在未排序队列的末尾

交换排序

相邻元素两两比较,如果不合适,则进行交换

堆排序

什么是堆排序?

查找

顺序查找

将整个数组遍历一遍

二分查找

折半查找

泛型程序设计与STL标准模板库

泛型程序设计

泛型程序设计,是为了提高代码复用率的方法。在解决同一类问题的过程中,抽象出一种概念,根据这种概念设计通用的算法。。

术语

  • 概念:具备一定功能的数据模型
  • 模型:符合一个概念的数据类型,称为该概念的模型

使用概念作为模板参数名,设计对应的类模板和函数模板,从而可以解决同一类问题。

STL标准模板库

基本组件

迭代器是算法和容器的桥梁,将函数对象作为算法的参数。

  • 迭代器:泛型指针,提供顺序访问容器对象的方法
  • 容器:容纳、包含一组元素的对象,并且通过适配器实现基本容器的特殊化
  • 算法:需要包含头文件,提供了70多种算法
  • 函数对象:行为类似函数的对象,任何普通函数以及重载"()"运算符的类对象都可以作为函数对象使用

迭代器

输入流迭代器

用来从序列中读取数据

用法

  • 以输入流为参数构造
  • 可以使用*(p++)获取下一个输入的元素

代码实例

istream_iterator<double>(cin)
输出流迭代器

用来向序列中写入数据

用法

  • 以输出流为参数构造
  • 可以使用*(p++) = x,将x输出到输出流

代码实例

ostream_iterator<double>(cout,"\t")
前向迭代器

既是输入流迭代器也是输出流迭代器,可以对序列进行单向遍历

双向迭代器

与前向迭代器类似,不过可以在两个方向遍历数据

随机访问迭代器

与双向迭代器类似,但是可以在任意位置进行跳转。vector容器的迭代器就是随机访问迭代器。

容器

根据组织方式,可分为:顺序容器、关联容器
根据访问方式,可分为:可逆容器(又可衍生出随机访问容器的概念)、不可逆容器

通用接口:

  • 默认构造函数
  • 关系运算符
  • begin(),end()
  • clear()
  • empty()
  • size()
  • swap()
顺序容器

举例:向量,双端队列,列表,单向链表,数组

通用接口:

  • asign()
  • insert()
  • resize()

向量(vector):

  • 可扩展的数组
  • 尾部插入比较快,中间插入比较慢

双端队列(deque):

  • 尾部和头部插入较快,中间插入较慢
  • 随机访问比向量慢

列表(List):

  • 任意位置插入和删除很快
  • 不支持随机访问

数组(array):

  • 对内部数组的封装
关联容器

特点:每个容器都有key,可根据key进行高效查找
分类:

  • 按照键对应的元素多少,可分为单重关联容器和多重关联容器
  • 按照类型参数的多少,可分为简单关联容器和二元关联容器
    举例:set,multiset,map,multimap
    通用接口:
  • insert()
  • erase()
  • find()
  • lower_bound(),upper_bound(),equal_range()
  • count()

集合(set):

  • key即是元素本身

映射(map):

  • 元素是由键和附加数据组成的二元组pair<type,type>
  • 可以使用下标运算
#include <iostream>
#include <map>
#include <cctype>
using namespace std;
int main() {
    map<char, int> s;   //用来存储字母出现次数的映射
    char c;     //存储输入字符
    do {
      cin >> c; //输入下一个字符
      if (isalpha(c)){ //判断是否是字母
          c = tolower(c); //将字母转换为小写
          s[c]++;      //将该字母的出现频率加1
      }
    } while (c != '.'); //碰到“.”则结束输入
    //输出每个字母出现次数
    for (map<char, int>::iterator iter = s.begin(); iter != s.end(); ++iter)
        cout << iter->first << " " << iter->second << "  ";
    cout << endl;
    return 0;
}

多重集合(multiset):

  • 允许有重复元素

多重映射(multimap):

  • 一个键有多个附加数据

函数对象

算法

流类库与输入输出

异常处理

异常:可以预测但是无法避免的错误。
使用独立异常处理模块的原因:1.使得程序整体逻辑连贯;2.小的功能模块没有权限处理错误;

异常处理语法

throw 块

throw: 抛出异常

try 块

try: 所有可能抛出异常的语句,在try块中运行,一旦抛出异常,就中断运行
catch: 捕获异常

:可在函数声明同时,声明异常类型,方便处理
程序抛掷A,B,C,D类型的异常

void fun() throw(A,B,C,D);

函数抛掷任意类型的异常

void fun();

函数不抛掷异常

void fun() throw();

异常处理机制会自动析构try块中构造的对象

标准异常类

exception
	|-logic_error: 可以在程序中被预先检测出的异常
		|-invalid_argument:
		|-domain_error:
	|-runtime_error: 难以被预先检测出的异常

异常安全性问题

异常安全性:异常发生时,既不泄露资源,也不能使任何对象陷入非法的状态。
原则:不抛掷异常是异常安全的基础
技巧:

  • swap()函数一定不会抛出异常;
  • 析构函数尽量不要抛出异常,否则在捕获异常时,析构栈上的对象,如果抛出异常,就会调用terminate函数;
  • 在抛出异常前,将函数中动态申请的内存释放(过于繁琐);
  • 尽量使用对象;
  • 使用智能指针auto_ptr指向动态申请的地址,那么当异常被捕获时,会自动析构智能指针,也就释放了动态申请的内存;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值