简单说明
主要是刚入手时将一些常见并且不懂的地方写在这块,有基础的同学直接从C++类这块开始看。
另外一些基础概念是从菜鸟社区搜索得来,还有一部分是跟随B站黑马程序员学习。希望详细了解同学可以去看看。
1、C++ 是区分大小写的编程语言
2、关键字
asm | else | new | this |
---|---|---|---|
auto | enum | operator | throw |
bool | explicit | private | true |
break | export | protected | try |
case | extern | public | typedef |
catch | false | register | typeid |
char | float | reinterpret_cast | typename |
class | for | return | union |
const | friend | short | unsigned |
const_cast | goto | signed | using |
continue | if | sizeof | virtual |
default | inline | static | void |
delete | int | static_cast | volatile |
do | long | struct | wchar_t |
double | mutable | switch | while |
dynamic_cast | namespace | template |
3、三字符组
三字符组就是用于表示另一个字符的三个字符序列,又称为三字符序列。三字符序列总是以两个问号开头。以前为了表示键盘上没有的字符。
4、数据类型
类型 | 位 | 范围 |
---|---|---|
char | 1 个字节 | -128 到 127 或者 0 到 255 |
unsigned char | 1 个字节 | 0 到 255 |
signed char | 1 个字节 | -128 到 127 |
int | 4 个字节 | -2147483648 到 2147483647 |
unsigned int | 4 个字节 | 0 到 4294967295 |
signed int | 4 个字节 | -2147483648 到 2147483647 |
short int | 2 个字节 | -32768 到 32767 |
unsigned short int | 2 个字节 | 0 到 65,535 |
signed short int | 2 个字节 | -32768 到 32767 |
long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
signed long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
unsigned long int | 8 个字节 | 0 到 18,446,744,073,709,551,615 |
float | 4 个字节 | 精度型占4个字节(32位)内存空间,+/- 3.4e +/- 38 (~7 个数字) |
double | 8 个字节 | 双精度型占8 个字节(64位)内存空间,+/- 1.7e +/- 308 (~15 个数字) |
long double | 16 个字节 | 长双精度型 16 个字节(128位)内存空间,可提供18-19位有效数字。 |
wchar_t | 2 或 4 个字节 |
枚举类型
变量的值只能在列举出来的值的范围内。
创建枚举,需要使用关键字 enum
enum 枚举名{ 标识符[=整型常数], 标识符[=整型常数], ... 标识符[=整型常数] } 枚举变量;
如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始。
例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 "blue"。
enum color { red, green, blue } c; c = blue;
默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,green 的值为 5。
enum color { red, green=5, blue };
在这里,blue 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1,但 red 的值依然为 0。
类型转换 静态转换、动态转换、常量转换和重新解释转换。
静态
静态转换不进行任何运行时类型检查
int i = 10; float f = static_cast<float>(i); // 静态将int类型转换为float类型
动态
通常用于将一个基类指针或引用转换为派生类指针或引用。动态转换在运行时进行类型检查,如果不能进行转换则返回空指针或引发异常。
class Base {}; class Derived : public Base {}; Base* ptr_base = new Derived; Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); // 将基类指针转换为派生类指针
重新解释转换
const int i = 10; int& r = const_cast<int&>(i); // 常量转换,将const int转换为int
重新解释转换将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换。
重新解释转换不进行任何类型检查,因此可能会导致未定义的行为。
5、C++ 中的左值(Lvalues)和右值(Rvalues)
C++ 中有两种类型的表达式:
-
左值(lvalue):*指向内存位置的表达式**被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。
-
右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。
变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。下面是一个有效的语句:
int g = 20;
但是下面这个就不是一个有效的语句,会生成编译时错误:
10 = 20;
6、C++ 中的类型限定符
限定符 | 含义 |
---|---|
const | const 定义常量,表示该变量的值不能被修改。。 |
volatile | 修饰符 volatile 告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程。。 |
restrict | 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。 |
mutable | 表示类中的成员变量可以在 const 成员函数中被修改。 |
static | 用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问。 |
register | 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率。 |
7、存储类
auto
声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。
auto f=3.14; //double auto s("hello"); //const char* auto z = new auto(9); // int* auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型
static
static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。
在 C++ 中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享
extern
extern 是用来在另一个文件中声明一个全局变量或函数
8、循环控制语句
break 语句 | 终止 loop 或 switch 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句。 |
---|---|
continue 语句 | 引起循环跳过主体的剩余部分,立即重新开始测试条件。 |
goto 语句 | 将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。 |
9、函数参数
传值调用 | 该方法把参数的实际值赋值给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。 |
---|---|
指针调用 | 该方法把参数的地址赋值给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 |
引用调用 | 该方法把参数的引用赋值给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 |
10、Lambda 函数与表达式
感兴趣的同学自行查询
11、cout
cout << "HELLOWORLD" << endl;
12、Cin
cin>>a;
13**"\n"** 代替以上代码里的 endl。
14、拷贝构造
是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
-
通过使用另一个同类型的对象来初始化新创建的对象。
-
复制对象把它作为参数传递给函数。
-
复制对象,并从函数返回这个对象。
拷贝构造函数的最常见形式如下:
classname (const classname &obj)
{ // 构造函数的主体 }
标准算法
所有不支持随机访问迭代器的容器不能够使用标准算法库
不支持随机访问迭代器的容器内部会提供对应的算法
C++类&对象
类中的数据称为成员变量,函数称为成员函数。类可以被看作是一种模板,可以用来创建具有相同属性和行为的多个对象。
c++类定义
关键字:class + 指定类明名称,并类的主体是包含在一对花括号中,主体包含类的成员变量和成员函数。
以下实例我们使用关键字 class 定义 Box 数据类型,包含了三个成员变量 length、breadth 和 height:
class Box { public: double length; double breadt; double height; }
关键字 public 确定了类成员的访问属性。在类对象作用域内,公共成员在类的外部是可访问的。您也可以指定类的成员为 private 或 protected。
定义 C++ 对象
Box Box1; // 声明 Box1,类型为 Box Box Box2; // 声明 Box2,类型为 Box
访问数据成员
类的对象的公共数据成员可以使用直接成员访问运算符 . 来访问。
#include <iostream> using namespace std; class Box { public: double length; // 长度 double breadth; // 宽度 double height; // 高度 // 成员函数声明 double get(void); void set( double len, double bre, double hei ); }; // 成员函数定义 double Box::get(void) { return length * breadth * height; } void Box::set( double len, double bre, double hei) { length = len; breadth = bre; height = hei; } int main( ) { Box Box1; // 声明 Box1,类型为 Box Box Box2; // 声明 Box2,类型为 Box Box Box3; // 声明 Box3,类型为 Box double volume = 0.0; // 用于存储体积 // box 1 详述 Box1.height = 5.0; Box1.length = 6.0; Box1.breadth = 7.0; // box 2 详述 Box2.height = 10.0; Box2.length = 12.0; Box2.breadth = 13.0; // box 1 的体积 volume = Box1.height * Box1.length * Box1.breadth; cout << "Box1 的体积:" << volume <<endl; // box 2 的体积 volume = Box2.height * Box2.length * Box2.breadth; cout << "Box2 的体积:" << volume <<endl; // box 3 详述 Box3.set(16.0, 8.0, 12.0); volume = Box3.get(); cout << "Box3 的体积:" << volume <<endl; return 0; }+
需要注意的是,私有的成员和受保护的成员不能使用直接成员访问运算符 (.) 来直接访问。我们将在后续的教程中学习如何访问私有成员和受保护的成员。
类成员函数 | 类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。 |
---|---|
类访问修饰符 | 类成员可以被定义为 public、private 或 protected。默认情况下是定义为 private。 |
构造函数 & 析构函数 | 类的构造函数是一种特殊的函数,在创建一个新的对象时调用。类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。 |
C++ 拷贝构造函数 | 拷贝构造函数,是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。 |
C++ 友元函数 | 友元函数可以访问类的 private 和 protected 成员。 |
C++ 内联函数 | 通过内联函数,编译器试图在调用函数的地方扩展函数体中的代码。 |
C++ 中的 this 指针 | 每个对象都有一个特殊的指针 this,它指向对象本身。 |
C++ 中指向类的指针 | 指向类的指针方式如同指向结构的指针。实际上,类可以看成是一个带有函数的结构。 |
C++ 类的静态成员 | 类的数据成员和函数成员都可以被声明为静态的。 |
继承
继承允许我们依据另一个类来定义一个类,当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
例子
// 基类 class Animal { // eat() 函数 // sleep() 函数 }; //派生类 class Dog : public Animal { // bark() 函数 };
一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:
#include <iostream> using namespace std; // 基类 class Shape { public: void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } protected: int width; int height; }; // 派生类 class Rectangle: public Shape { public: int getArea() { return (width * height); } }; int main(void) { Rectangle Rect; Rect.setWidth(5); Rect.setHeight(7); // 输出对象的面积 cout << "Total area: " << Rect.getArea() << endl; return 0; }#include <iostream> using namespace std; // 基类 class Shape { public: void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } protected: int width; int height; }; // 派生类 class Rectangle: public Shape { public: int getArea() { return (width * height); } }; int main(void) { Rectangle Rect; Rect.setWidth(5); Rect.setHeight(7); // 输出对象的面积 cout << "Total area: " << Rect.getArea() << endl; return 0; }
派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。
我们可以根据访问权限总结出不同的访问类型,如下所示:
访问 | public | protected | private |
---|---|---|---|
同一个类 | yes | yes | yes |
派生类 | yes | yes | no |
外部的类 | yes | no | no |
继承类型
当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。
我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:
-
公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
-
保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
-
私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
多继承
多继承即一个子类可以有多个父类,它继承了多个父类的特性。
C++ 类可以从多个类继承成员,语法如下:
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,… { <派生类类体> };
class Rectangle: public Shape, public PaintCost { public: int getArea() { return (width * height); } };
C++ 重载运算符和重载函数
可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。
函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
也就是重新定义运算,对类进行运算。
C++ 中的运算符重载
可重载运算符/不可重载运算符
下面是可重载的运算符列表:
双目算术运算符 | + (加),-(减),*(乘),/(除),% (取模) |
---|---|
关系运算符 | ==(等于),!= (不等于),< (小于),> (大于),<=(小于等于),>=(大于等于) |
逻辑运算符 | ||(逻辑或),&&(逻辑与),!(逻辑非) |
单目运算符 | + (正),-(负),*(指针),&(取地址) |
自增自减运算符 | ++(自增),--(自减) |
位运算符 | | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移) |
赋值运算符 | =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>= |
空间申请与释放 | new, delete, new[ ] , delete[] |
其他运算符 | ()(函数调用),->(成员访问),,(逗号),[](下标) |
多态
当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
有了多态,您可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。
虚函数
在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
即派生类中可定义在基类中定义的函数,,调用时候调用派生类中定义的函数。
纯虚函数
想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } // pure virtual function virtual int area() = 0; };
= 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数。
数据抽象
在 C++ 中,我们使用类来定义我们自己的抽象数据类型(ADT)。您可以使用类 iostream 的 cout 对象来输出数据到标准输出
访问标签强制抽象
-
使用公共标签定义的成员都可以访问该程序的所有部分。一个类型的数据抽象视图是由它的公共成员来定义的。
-
使用私有标签定义的成员无法访问到使用类的代码。私有部分对使用类型的代码隐藏了实现细节。
数据封装
C++接口
如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 "= 0" 来指定的,
设计抽象类(通常称为 ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。抽象类不能被用于实例化对象,它只能作为接口使用。
如果一个 ABC 的子类需要被实例化,则必须实现每个纯虚函数,这也意味着 C++ 支持使用 ABC 声明接口。如果没有在派生类中重写纯虚函数,就尝试实例化该类的对象,会导致编译错误。
c++函数重载与构造函数
函数名称可以相同,提高复用性
条件:
1、同一个作用域
2、函数名称相同
3、函数参数类型不同或者个数不同或者顺序不同
函数重载注意事项
1、引用
int &a =10;//不合法 const int &a=10;//合法
2、函数重载碰到默认参数
出现二义性,尽量避免出现默认参数
构造函数和构析函数
构造函数 类名(){}
析构函数 ~类名(){},析构函数不能有参数
构造和析构必须有,没有编译器自动空实现,即函数为空
构造函数
1、没有返回值,不用写void
2、函数名与类名相同
3、构造函数可以有参数,可以发生重载
4、创建对象 的时候,构造函数会自动调用,而且只调用一次
析构
1、没有返回值,不用写void
2、函数名与类名相同,在前面加~
3、函数没有参数,不能重载
4、对象销毁前,会自动调用析构函数,智能调用一次
构造函数的分类及调用
1、分类
无参数构造(默认)和有参数构造
普通构造函数和拷贝构造函数
拷贝函数写法
public: Person(const Person &p)//使用常量,防止原person修改 { age=p.age } 将传入的Person 传入到当前Person
1、括号法:
Person p1;//默认无参 Person p2(10);//有参 Person p3(p2);//拷贝函数
注意: 默认构造函数使用时,不需要加();否则编译器会认为是函数声明,不会认为是创建对象
2、显示法
Person p1; Person p2=Person(10); Person p3=Person(p2);
等号右方称为匿名对象,当前行执行结束后系统自动回收匿名对象;
不要利用拷贝构造函数初始化匿名对象:Person(P3),等价于Person p3;会重定义p3;
3、隐式转换法
Person p4 = 10; Person p5 = p4;
拷贝构造函数调用时机
1、已经创建完毕的对象来初始化一个对象
2、值传递的方式给函数参数传值
Person p1,p1作为实参给形参传递会调用拷贝函数
3、值方式返回局部对象
函数返回值是p
构造函数调用规则
1、如果用户定义有参构造函数,c++不再提供无参构造,但默认提供拷贝构造
2、自定义拷贝函数,不再提供其他构造函数
深拷贝与前拷贝
浅拷贝:简单复制拷贝操作,不重新开辟内存空间,执行析构代码释放空间时可能会报错。堆区的内存重复释放。
深拷贝:在堆区重新申请空间,进行拷贝操作。
c++模板
函数模板和类模板
函数模板
template<typename T>函数声明或定义,T是一个通用数据类型
template 声明创建模板
typename ,可用class代替,后面是一种数据类型
void mySwap(T &a,T &b){ T temp; temp=a; a=b; b=temp; } //自动推导类型 int main() { int a=10; int b=20; char c='c'; //myswap(a,b);//自动识别T的类型为整形 //myswap<int>(a,b)//显示指定类型 }
注意
推导的T类型应保持一致
模板必须确定T的数据类型才可以使用。
对不同类型的数组进行排序用char和int进行测试
//对不同类型的数组进行排序用char和int进行测试 #include <iostream> using namespace std; template <class T> void printarray(T arr[], int len) { for (int i = 0;i < len;i++) { cout << arr[i] << " "; } } template <class T> void myswap(T&a, T&b) { T temp= a; a = b; b = temp; } template <class T> void sort(T arr[], int len) { for (int i = 0;i < len;i++) { int max = i; for (int j = i + 1;j < len;j++) { if (arr[max] < arr[j]) { max = j; } } if (max!= i) { myswap(arr[max], arr[i]); } } } void tset01() { char charArr[] = "badcfe"; int num = sizeof(charArr) / sizeof(char); sort(charArr,num); printarray(charArr, num); } void test02() { int intArr[] = {7,123,18,186,894,66}; int num = sizeof(intArr) / sizeof(int); sort(intArr, num); printarray(intArr, num); } int main() { tset01(); test02(); }
普通函数与函数模板的区别
1、普通函数调用可以发生隐式类型转换,即将实参类型转换为形参类型计算。
2、函数模板同自动类型推导,不可以发生隐式类型转换
3、函数模板用显示指定类型,可以发生隐式类型转换
普通函数与函数模板调用规则
(两个模板名称相同)
1、如果函数模板和普通模板都可以调用,优先调用普通函数
2、可以通过空模板参数列表强制调用函数模板(空模板即模板后<>中声明指定类型不填)
3、函数模板可以发生函数重载
4、如果函数模板可以产生更好的匹配,优先调用函数模板
模板的局限性
利用具体化的模板,可以解决自定义类型的通用化 即对函数进行重构
学习模板是为了在stl能偶运用系统提供的模板
1.3类模板
通用格式:
template<typename T>
类
template<class NameType,class AgeType> class Person { public: PErson(NameType,name,AgeType age) { thie->m_name=name; this->m_age=age; } Nametype m_name; AgeType m_age; }; void test() { Person<string,int> p1("孙悟空",999); //Person("孙悟空",999)会报错 }
注意:类模板没有自动类型推导
类模板在模板参数列表可以有默认参数
Person("孙悟空",999)会报错 Person<string,int> p1("孙悟空",999);正常 若 template<class NameType,class AgeType=int> Person<string> p1("孙悟空",999);正常
类模板中成员函数创建时机
类模板中成员函数在调用时才去创建
类模板对象做函数参数
1、指定传入的类型
void printperson1(Person<string, int>&p) { p.showperson(); }
2、参数模板化
template<class T1,class T2> void printperson2(Person<T1, T2>& p) { p.showperson(); cout << typeid(T1).name() << endl; cout << typeid(T2).name() << endl; } //typeid(T1).name() 显示数据类型
3、整个类模板化
template<class T> void printperson3(T &p) { p.showperson(); }
T为calss Person 类型
类模板与继承
1、当子类继承的父类是一个类模板时,子类在声明的时候,要制定出父类中T的类型
template<class T> class base { T m; }; class Son :: public base<int> { };
将子类也变为类模板,灵活制定T
template<class T1,class T2> class Son :: public base<T2> { T1 obj; };
2、如果不指定,编译器无法给子类分配内存
3、如果想灵活指出父类中T的类型,子类也需变为类模板
类模板成员函数类外实现
template<class T1,class T2> class Person { public: Person(T1 name,T2 age); // { // this->m_name = name; // this->m_age = age; } void showperson(); // { // cout << "姓名" << this->m_name << "年龄" << this->m_age << endl; // } T1 m_name; T2 m_age; }; 构造函数类外实现 template<class T1,class T2> Person<T1,T2>::Person(T1 name,T2 age) { this->m_name = name; this->m_age = age; } 成员函数类外实现 template<class T1,class T2> void Person<T1,T2>::showperson() { cout << "姓名" << this->m_name << "年龄" << this->m_age << endl; }
类模板分文件编写
1、直接包含.cpp
2、将声明和实现写在.hpp文件中
类模板与友元(全局函数类内类外实现)
全局函数类外实现不需要作用域,需要在声明处加空模板参数列表即<>,还需要在声明前,并且要知道Person类,即Person重新声明。
template<class T1, class T2> class Person; template<class T1, class T2> void printperson2(Person<T1, T2>p) { cout << "姓名" << p.m_name << "年龄" << p.m_age; }
c++STL
容器 算法 迭代器 仿函数 适配器(配接器) 空间配置器
容器
序列式容器:强调值的排序,每个元素都有固定的位置
关联式容器:二叉树结构个元素之间没有严格的物理上的顺序关系。
算法 AIgorithms
质变算法:拷贝 替换 删除
非质变算法:查找 计数 遍历 寻找极值
迭代器:iterator
容器和算法之间的桥梁
输入 输入 前向 双向 随机迭代器
容器算法初识
vector存放内置数据类型
容器:vector
算法:for_each
迭代器:vector<<int>::iterator
void test1() { vector<int> v; //向容器中插入数据 v.push_back(10); v.push_back(20); v.push_back(30); v.push_back(40); v.push_back(50); //vector<int>::iterator itbegin = v.begin(); //vector<int>::iterator itend = v.end();//指向容器最后一个元素的下一个位置 //while (itbegin != itend) //{ // cout << *itbegin << endl; // itbegin++; //} //*********************************** //for (vector<int>::iterator it = v.begin();it != v.end();it++) //{ // cout << *it <<endl;; //} //*********************************** for_each(v.begin(), v.end(), myprint); }
vector存放自定义类型
void test11() { vector<person>v; person p1("aaa", 10); person p2("aaa", 20); person p3("aaa", 30); person p4("aaa", 40); person p5("aaa", 50); v.push_back(p1); v.push_back(p2); v.push_back(p3); v.push_back(p4); v.push_back(p5); for (vector<person>::iterator it = v.begin();it != v.end();it++) { cout << (*it).mname << (*it).mage<< endl; } } void test02() { vector<person*>v; person p1("aaa", 10); person p2("aaa", 20); person p3("aaa", 30); person p4("aaa", 40); person p5("aaa", 50); v.push_back(&p1); v.push_back(&p2); v.push_back(&p3); v.push_back(&p4); v.push_back(&p5); for (vector<person*>::iterator it = v.begin();it != v.end();it++) { cout << (*it)->mage<< (*it)->mname << endl; } }
容器中嵌套容器
相当于二维数组,将所有数据进行遍历输出
void test01() { vector<vector<int>>v; vector<int>v1; vector<int>v2; vector<int>v3; vector<int>v4; for (int i = 0;i < 4;i++) { v1.push_back(i + 1); v2.push_back(i + 2); v3.push_back(i + 3); v4.push_back(i + 4); } v.push_back(v1); v.push_back(v2); v.push_back(v3); v.push_back(v4); for (vector<vector<int>>::iterator it = v.begin();it != v.end();it++) { for (vector<int>::iterator it2 = (*it).begin();it2 != (*it).end();it2++) { cout << *it2<<"\t"; } cout <<endl; } }
string 容器
char *是一个指针
string 是一个类,类内封装了char*
string 赋值操作
1、用等号
2、用assign赋值
###
vector数组
1、可以动态扩展,并不是在原有空间内存后续新接,是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
2、vector叠加器可以随机访问
vector(v.begin(),v.end());将[being,end)范围内的数据
vector<int>(10,100),10是个数,100是数据,即10个100
vector赋值操作
vector =
assign(beg,end),左闭右开
assign(num,data)
vector容量和大小
empty
capacity
size
resize(int num)
resizie(int num,elem)
容量永远大于大小
如果重新指定长度比原来长,默认用0填充,如果比原来短,,那么超出的部分会被删除掉
插入和删除
push_back//尾部插入
pop_back //尾部删除
insert(const_interator pos,els) //第一个参数是迭代器v.begin(),,第二是数据
insert(const_interator pos,int count,els) //count是插入个数
erase(const_interator pos)//迭代器
erase(const_iterator start,const_iterator end)//迭代器,提供区间
clear() //清空函数
数据存取
vector<int> v1; 1、V1[i] 2、v1.at(i) 3、获取第一个元素v.front() 最后一个v.back()
使用迭代器输出二维vector
for (vector<vector<int>>::iterator it = ans.begin();it != ans.end();it++) { for (vector<int>::iterator it1 = (*it).begin();it1 != (*it).end();it1++) { cout << *it1 << " "; } cout << endl; }
互换容器
swap()
巧用swap可以收缩内存空间
使用resize后容器大小改变,容量不改变
vector<int>(v).swap(v);vector<int>(v)为匿名对象,按照v来初始化匿名对象,然后和v进行交换
预留空间
减少vector在动态扩展时容量的扩展次数
reserve(int len);
list容器
链表,物理存储单元不连续,由数据域和指针域组成
优点:可以对任意的位置进行插入或删除元素,采用动态存贮分配,不会造成内存的浪费
缺点:容器遍历速度没有数组快,占用的空间大
stl中的链表是一个双向循环链表
构造函数
list<T> list
list(beg,end)
list(num,ele)
void printlist(const list<int> &L) { for (list<int>::const_iterator it = L.begin();it != L.end();it++) { cout << *it << " "; } cout << endl; } //打印函数
大小操作
size()
empty()
resize(num)
resize(num,ele)
插入和删除
push_back(elem); //在容器尾部加入一个元素
pop_back(); //删除容器最后一个元素
push_fron(elem); //在容器开头插入一个元素
pop_front(); //从容器中移除第一个容器
insert(pos, elem); //在pos位置插入elem元素的拷贝,返回新数据的位置。
insert(pos, n, elem); //在pos位置插入n个emel数据,无返回值
insert(pos, beg, end); //在pos位置插入[eng,end]区间的数据,无返回值。
clear(); //一出容器中的所有数据
erase(beg, end); //删除[beg,end]区间的数据,返回下一个数据的位置
erase(pos); //删除pos位置的数据,返回下一个数据的位置
remove(elem); //删除容器中所有与elem值匹配的元素
数据存取
front();返回第一个元素
back()返回最后一个元素
L[i],和L.at(i)不能够访问元素,不支持随机访问
反转和排序
reverse()反转链表
sort()排序//默认是升序,
sort()
map/multimap容器
1、所有的元素都是pair
2、pair第一个元素是key值,起索引作用,第二个值是value,实际值
3、所有元素都会根据元素的键值自动排序
属于关联式容器,底层结构是二叉树实现
map不允许有重复的key值, multimap允许有重复的key值,
构造和赋值
map大小和交换
size
empty
swap
插入和删除
-
insert(elem); //在容器中插入元素
-
clear(); //清除所有元素
-
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器
-
erase(begin,end); //删除区间 [ begin, end) 的所有元素,返回下一个元素的迭代器
erase(key); //删除容器中值为key的元素
map查找和统计
find(key),返回一个迭代器
map<int,int> iterator:: it=find(i);如果未找到则返回m.end();
map容器中 count(i),统计容器中键值k的个数。返回值要么是0要么是1
map容器排序
利用仿函数可以指定容器的排序规则
对于自动逸的数据类型,map必须指定排序规则,同set容器·
stack容器(栈)
常用接口
stack<t> stk;
stack(congst stack &stk)/拷贝构造
赋值
=
数据存取
push
pop//出栈
top//返回栈顶元素
大小操作
empty
size()