C++自学笔记

简单说明

主要是刚入手时将一些常见并且不懂的地方写在这块,有基础的同学直接从C++类这块开始看。

另外一些基础概念是从菜鸟社区搜索得来,还有一部分是跟随B站黑马程序员学习。希望详细了解同学可以去看看。

1、C++ 是区分大小写的编程语言

2、关键字

asmelsenewthis
autoenumoperatorthrow
boolexplicitprivatetrue
breakexportprotectedtry
caseexternpublictypedef
catchfalseregistertypeid
charfloatreinterpret_casttypename
classforreturnunion
constfriendshortunsigned
const_castgotosignedusing
continueifsizeofvirtual
defaultinlinestaticvoid
deleteintstatic_castvolatile
dolongstructwchar_t
doublemutableswitchwhile
dynamic_castnamespacetemplate

3、三字符组

三字符组就是用于表示另一个字符的三个字符序列,又称为三字符序列。三字符序列总是以两个问号开头。以前为了表示键盘上没有的字符。

4、数据类型

类型范围
char1 个字节-128 到 127 或者 0 到 255
unsigned char1 个字节0 到 255
signed char1 个字节-128 到 127
int4 个字节-2147483648 到 2147483647
unsigned int4 个字节0 到 4294967295
signed int4 个字节-2147483648 到 2147483647
short int2 个字节-32768 到 32767
unsigned short int2 个字节0 到 65,535
signed short int2 个字节-32768 到 32767
long int8 个字节-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
signed long int8 个字节-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned long int8 个字节0 到 18,446,744,073,709,551,615
float4 个字节精度型占4个字节(32位)内存空间,+/- 3.4e +/- 38 (~7 个数字)
double8 个字节双精度型占8 个字节(64位)内存空间,+/- 1.7e +/- 308 (~15 个数字)
long double16 个字节长双精度型 16 个字节(128位)内存空间,可提供18-19位有效数字。
wchar_t2 或 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++ 中的类型限定符

限定符含义
constconst 定义常量,表示该变量的值不能被修改。。
volatile修饰符 volatile 告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程。。
restrictrestrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 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 语句终止 loopswitch 语句,程序流将继续执行紧接着 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 + 指定类明名称,并类的主体是包含在一对花括号中,主体包含类的成员变量和成员函数。

img

以下实例我们使用关键字 class 定义 Box 数据类型,包含了三个成员变量 length、breadth 和 height:

class Box
{
    public:
        double length;
        double breadt;
        double height;
}

关键字 public 确定了类成员的访问属性。在类对象作用域内,公共成员在类的外部是可访问的。您也可以指定类的成员为 privateprotected

定义 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。

我们可以根据访问权限总结出不同的访问类型,如下所示:

访问publicprotectedprivate
同一个类yesyesyes
派生类yesyesno
外部的类yesnono

继承类型

当一个类派生自基类,该基类可以被继承为 public、protectedprivate 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。

我们几乎不使用 protectedprivate 继承,通常使用 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)。您可以使用类 iostreamcout 对象来输出数据到标准输出

访问标签强制抽象

  • 使用公共标签定义的成员都可以访问该程序的所有部分。一个类型的数据抽象视图是由它的公共成员来定义的。

  • 使用私有标签定义的成员无法访问到使用类的代码。私有部分对使用类型的代码隐藏了实现细节。

数据封装

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()

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值