CPP refresh 内存模型 代码区 存放函数体的二进制代码,由操作系统进行管理
全局区 存放全局变量和静态变量以及常量.
栈区 
由编译器自动分配释放 
存放函数的参数值 
存放局部变量 
 
堆区 由程序员分配和释放,若不手动释放,最后将由操作系统回收
程序运行前 程序编译后,生成 exe 可执行程序,未执行该程序前分为两个区域 
代码区:
存放 CPU 执行的机器指令 
代码区是共享的,共享的的目的是对于频繁被执行的程序,只需要在内存中由一份代码即可 
代码区为 只读 ,防止程序意外地修改了它的指令 
 
 
全局区
全局变量和静态变量存放在此 
常量区 
字符串 
其他常量 
 
 
 
==该区域的数据在程序结束后由操作系统释放==
程序运行后 栈区:由编译器自动分配释放,存放函数参数值,局部变量
注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
new 操作符 CPP 利用 new 操作符在堆区开辟数据
语法:new 数据类型
==利用 new 创建的数据,会返回该数据对应的类型的指针==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include  <iostream>  using  namespace  std ;int * func () 	int * a = new  int (10 ); 	return  a; } int  main (int  argc, char ** argv) 	int  *p = func(); 	cout  << *p << endl ; 	cout  << *p << endl ; 	cout  << *p << endl ; 	delete  p; 	return  0 ; } 
开辟数组 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int  main ()  	int * arr = new  int [10 ]; 	for  (int  i = 0 ; i < 10 ; i++) 	{ 		arr[i] = i + 100 ; 	} 	for  (int  i = 0 ; i < 10 ; i++) 	{ 		cout  << arr[i] << endl ; 	} 	 	delete [] arr; 	return  0 ; } 
引用 引用是可以作为函数返回值存在的
注意不要返回局部变量引用
函数变量可以作为左值,则必须为返回引用类型(指针)
引用可以作为函数的返回值存在 ⚠️:不要在函数里返回局部变量
如果函数作为左值,则此被作为左值的函数必须返回为引用类型 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include  <iostream>  using  namespace  std ;int & quoteTest () 	static  int  a = 10 ; 	return  a; } int  main (int  argc, char ** argv) 	int & ret = quoteTest(); 	cout <<"a: " << ret<<endl ; 	cout <<"a: " << ret<<endl ; 	quoteTest() = 1000 ; 	cout <<"modified ret: " << ret<<endl ; 	cout <<"modified ret: " << ret<<endl ; 	return  0 ; } 
引用的本质 本质:引用的本质在 C++ 内部实现是一个指针常量 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void  func (int & ref) 	ref = 100 ;  } int  main () 	int  a = 10 ;      	int & ref = a; 	ref = 20 ;  	cout  << "a:"  << a << endl ; 	cout  << "ref:"  << ref << endl ; 	func(a); 	return  0 ; } 
常量引用 ==常量引用主要用来修饰形参,防止误操作==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include  <iostream>  using  namespace  std ;void  showValue (const  int  & val) 	cout  << val << endl ; } int  main (int  argc, char ** argv) 	int  temp = 10 ; 	const  int & test = temp; 	showValue(temp); 	showValue(test); 	return  0 ; } 
函数提高 函数占位参数示例 1 2 3 4 5 6 7 8 void  func (int  a, int )    cout  <<"pass" <<; } int  main (int  argc, char **argv)   func(10 , 10 );	   returen 0 ; } 
函数重载 函数名字可以相同,提高复用性。
重载满足条件:
同一个作用域下 
函数名称相同 
函数的参数类型不同,或者个数不同,或者顺序不同 
 
注意:函数的返回值不可以作为函数重载的条件 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 void  func () 	cout  << "func 的调用!"  << endl ; } void  func (int  a) 	cout  << "func (int a) 的调用!"  << endl ; } void  func (double  a) 	cout  << "func (double a)的调用!"  << endl ; } void  func (int  a ,double  b) 	cout  << "func (int a ,double b) 的调用!"  << endl ; } void  func (double  a ,int  b) 	cout  << "func (double a ,int b)的调用!"  << endl ; } int  main ()  	func(); 	func(10 ); 	func(3.14 ); 	func(10 ,3.14 ); 	func(3.14  , 10 ); 	system("pause" ); 	return  0 ; } 
函数重载注意事项 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 void  func (int  &a) 		  cout  << "func (int &a)"  << endl ; } void  func (const  int  &a)  	  cout  << "func(const int &a)"  << endl ; } void  funcc (int  a, int  b = 10 )   cout  << "..."  << endl ; } void  funcc (int  a )   cout  << "..."  << endl ; } int  main ()   int  a = 10 ;      func(a);		   func(10 );		       	return  0 ; } 
类和对象 ==封装、继承、多态==
设计一个圆,求圆的周长 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const  double  PI = 3.1415926535 ;class  Circle {   public :     int  radius;     double  calculatePT ()       {      return  2  * PI * radius;     } }; int  main ()   Circle cl;   cl.radius = 10 ;   cout  << "perimeter is: "  << cl.calculatePT()<<endl ; } 
设计一个学生类,有姓名和学号,可以给姓名和学号赋值,显示学生姓名和学号 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class  Student {   public :   	void  setName (String  name)       {      this ->name = name;     }   	void  setId (int  id)       {      this ->id = id;     }   	void  showStudent ()       {      cout  << "name: " << name << "id: " << id<<endl ;     }   public :   	String  name;   	int  id; }; int  main ()   Student stu;   stu.setName("nike" );   stu.setId(123 );   stu.showStudent();   return  0 ; } 
封装权限: public :公共权限,类内可以访问,类外也可以访问
protected:保护权限,类内可以访问,类外不可以访问
private:私有权限,类内可以访问,类外不可以访问
struct 与 class  区别 二者的唯一区别在于默认的访问权限的不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class  C1 { 	int   m_A;  }; struct  C2 { 	int  m_A;   }; int  main ()  	C1 c1; 	c1.m_A = 10 ;  	C2 c2; 	c2.m_A = 10 ;  	system("pause" ); 	return  0 ; } 
对象的初始化和清理 C++ 利用构造函数和析构函数解决对象的初始化和清理两个重要问题。
如果不手动提供构造函数和析构函数,编译器会提供 
构造函数语法:类名(){}
构造函数,没有返回值,不写void 
函数名称与类名称相同 
构造函数可以有参数,可以发生重载 
程序在调用对象时候会自动调用构造,且只调用一次 
 
析构函数语法:~类名(){}
析构函数没有返回值不写void 
函数名与类名相同,前加 ~ 
析构函数不可以有参数,不发生重载 
程序在对象销毁前自动调用析构,无需手动调用,且只调用一次 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class  Person { public :	 	Person() 	{ 		cout  << "Person的构造函数调用"  << endl ; 	} 	 	~Person() 	{ 		cout  << "Person的析构函数调用"  << endl ; 	} }; void  test01 () 	Person p; } int  main ()  	test01(); 	return  0 ; } 
构造函数中的拷贝构造函数 1 2 3 4 5 6 7 8 9 10 class  Person {  public :   	Person(const  Person& p)     {       age = p.age;       cout  << "拷贝构造函数" << endl ;     }   public :   	int  age; }; 
//注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
//注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 class  Person  {public :	 	Person() { 		cout  << "无参构造函数!"  << endl ; 	} 	 	Person(int  a) { 		age = a; 		cout  << "有参构造函数!"  << endl ; 	} 	 	Person(const  Person& p) { 		age = p.age; 		cout  << "拷贝构造函数!"  << endl ; 	} 	 	~Person() { 		cout  << "析构函数!"  << endl ; 	} public :	int  age; }; void  test01 ()  	Person p;  } void  test02 ()  	 	Person p1 (10 )  ; 	 	 	 	 	Person p2 = Person(10 ); 	Person p3 = Person(p2); 	Person(10 ) 	 	Person p4 = 10 ;  	Person p5 = p4;  	 	 } int  main ()  	test01(); 	 	system("pause" ); 	return  0 ; } 
拷贝构造函数调用时机 
使用一个已经创建完毕的对象来初始化一个新对象
值传递的方式给函数参数传值
1 2 3 4 5 6 7 8 9 void  doWork (Person p1)  } void  test02 ()  	Person p;  	doWork(p); } 
以值方式返回局部对象
1 2 3 4 5 6 7 8 9 10 11 12 13 Person doWork2 ()  	Person p1; 	cout  << (int  *)&p1 << endl ; 	return  p1; } void  test03 () 	Person p = doWork2(); 	cout  << (int  *)&p << endl ; } 
 
构造函数调用规则 默认情况下,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造 
 
如果用户定义拷贝构造函数,c++不会再提供其他构造函数 
 
深拷贝与浅拷贝 浅拷贝:简单的赋值拷贝
深拷贝:在堆区重新申请空间,进行拷贝操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 class  Person  {public :	 	Person() { 		cout  << "无参构造函数!"  << endl ; 	} 	 	Person(int  age ,int  height ) { 		cout  << "有参构造函数!"  << endl ; 		m_age = age; 		m_height = new  int (height ); 	} 	 	Person(const  Person& p) { 		cout  << "拷贝构造函数!"  << endl ; 		 		m_age = p.m_age;      		m_height = new  int (*p.m_height);	 	} 	 	~Person() { 		cout  << "析构函数!"  << endl ; 		if  (m_height != NULL ) 		{ 			delete  m_height; 		} 	} public :	int  m_age; 	int * m_height;	 }; void  test01 () 	Person p1 (18 , 180 )  ; 	Person p2 (p1)  ; 	cout  << "p1的年龄: "  << p1.m_age << " 身高: "  << *p1.m_height << endl ; 	cout  << "p2的年龄: "  << p2.m_age << " 身高: "  << *p2.m_height << endl ; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
总结:如果属性有在 堆区 开辟的,一定要自己提供拷贝函数,防止浅拷贝带来的问题 
初始化列表 1 2 3 4 5 6 7 8 9 10 11 12 	 	Person(int  a, int  b, int  c) :m_A(a), m_B(b), m_C(c) {} 	void  PrintPerson ()   		cout  << "mA:"  << m_A << endl ; 		cout  << "mB:"  << m_B << endl ; 		cout  << "mC:"  << m_C << endl ; 	} private :	int  m_A; 	int  m_B; 	int  m_C; }; 
类对象作为类成员 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 class  Phone { public :	Phone(string  name) 	{ 		m_PhoneName = name; 		cout  << "Phone构造"  << endl ; 	} 	~Phone() 	{ 		cout  << "Phone析构"  << endl ; 	} 	string  m_PhoneName; }; class  Person { public :	 	Person(string  name, string  pName) :m_Name(name), m_Phone(pName) 	{ 		cout  << "Person构造"  << endl ; 	} 	~Person() 	{ 		cout  << "Person析构"  << endl ; 	} 	void  playGame ()  	 {		cout  << m_Name << " 使用"  << m_Phone.m_PhoneName << " 牌手机! "  << endl ; 	} 	string  m_Name; 	Phone m_Phone; }; void  test01 () 	 	 	 	Person p ("张三"  , "苹果X" )  ; 	p.playGame(); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
//初始化列表可以告诉编译器调用哪一个构造函数
Person(string name, string pName) :m_Name(name), m_Phone(pName)
{
    cout << "Person构造" << endl;
}
Phone m_Phone;
以上两处,构造函数中 pName 为 string 类型, m_Phone 为 Phone 对象类型,其实是构造函数的隐式调用
// Phone m_Phone = pName;
等价于以下:
// Phone m_Phone = Phone(pName);
静态成员 静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员分为:
静态成员变量
所有对象共享同一份数据 
在编译阶段分配内存 
类内声明,类外初始化 
 
 
静态成员函数
所有对象共享同一个函数 
静态成员函数只能访问静态成员变量 
 
 
 
静态成员变量: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class  Person { public :	static  int  m_A;  	 	 	 	 private :	static  int  m_B;  }; int  Person::m_A = 10 ;int  Person::m_B = 10 ;void  test01 () 	 	 	Person p1; 	p1.m_A = 100 ; 	cout  << "p1.m_A = "  << p1.m_A << endl ; 	Person p2; 	p2.m_A = 200 ; 	cout  << "p1.m_A = "  << p1.m_A << endl ;  	cout  << "p2.m_A = "  << p2.m_A << endl ; 	 	cout  << "m_A = "  << Person::m_A << endl ; 	 } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
静态成员函数: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class  Person { public :	 	 	 	static  void  func ()  	 {		cout  << "func调用"  << endl ; 		m_A = 100 ; 		 	} 	static  int  m_A;  	int  m_B;  private :	 	static  void  func2 ()  	 {		cout  << "func2调用"  << endl ; 	} }; int  Person::m_A = 10 ;void  test01 () 	 	 	Person p1; 	p1.func(); 	 	Person::func(); 	 } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
this 指针指向被调用的成员函数所属的对象 this 指针的用途 
==当形参和变量名相同时,可用 this 来进行区分,其指向类所有的成员变量== 
==在类的非静态成员函数中返回对象本身,可以使用 return *this==` 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include  <iostream>  using  namespace  std ;class  Person { public :	Person(int  age) { 		this ->age = age; 	} 	Person& PersonAddPerson (Person p)   { 		this ->age += p.age; 		return  *this ; 	} public :	int  age; }; void  test01 ()  	Person p1 (10 )  ; 	cout  << "p1.age= "  << p1.age << endl ; 	Person p2 (10 )  ; 	p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1); 	cout  << "after add, the value of p2= "  << p2.age << endl ; } int  main () 	test01(); 	system("pause" ); 	return  0 ; } 
空指针访问成员函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class  Person  {public :	void  ShowClassName ()   		cout  << "我是Person类!"  << endl ; 	} 	void  ShowPerson ()   		if  (this  == NULL ) { 			return ; 		} 		cout  << mAge << endl ; 	} public :	int  mAge; }; void  test01 () 	Person * p = NULL ; 	p->ShowClassName();  	p->ShowPerson();   } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
const 修饰成员函数 常函数 
按理说在设计类之前,就应该预先设置好一个函数是否为常函数。
成员函数后加 const 即为常函数 
常函数内不可以进行修改成员属性 
==成员属性声明时加关键字 mutable 后,常函数中依然可以进行修改== 
 
常对象 
声明对象前加 const 称该对象为常对象 
常对象只能调用常函数 
 
友元 友元的关键字为 ==friend==
同一个 class 生成的 object 互为友元。
友元的三种实现 
1 2 3 4 5 6 7 8 9 10 11 12 class  Building { 	 	friend  void  goodGay (Building * building)  public :	Building() 	{ 		this ->m_SittingRoom = "客厅" ; 		this ->m_BedRoom = "卧室" ; 	} 
1 2 3 4 5 6 7 8 9 10 11 12 public :	string  m_SittingRoom;  private :	string  m_BedRoom;  }; void  goodGay (Building * building) 	cout  << "好基友正在访问: "  << building->m_SittingRoom << endl ; 	cout  << "好基友正在访问: "  << building->m_BedRoom << endl ; } 
1 2 3 4 5 6 7 8 9 10 11 12 void  test01 () Building b; goodGay(&b); } int  main () test01(); system("pause" ); return  0 ;} 
类做友元1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 将一个类作为另一个类的友元,即可以访问类中的私有成员。 ```c++ class Building; class goodGay { public: 	goodGay(); 	void visit(); private: 	Building *building; }; class Building { 	//告诉编译器 goodGay类是Building类的好朋友,可以访问到Building类中私有内容 	friend class goodGay; public: 	Building(); public: 	string m_SittingRoom; //客厅 private: 	string m_BedRoom;//卧室 }; // 在类外写成员函数 Building::Building() { 	this->m_SittingRoom = "客厅"; 	this->m_BedRoom = "卧室"; } goodGay::goodGay() { 	building = new Building; } void goodGay::visit() { 	cout << "好基友正在访问" << building->m_SittingRoom << endl; 	cout << "好基友正在访问" << building->m_BedRoom << endl; } void test01() { 	goodGay gg; 	gg.visit(); } int main(){ 	test01(); 	system("pause"); 	return 0; } 
 
 
  class Building
    {
        //告诉编译器  goodGay类中的visit成员函数 是Building好朋友,可以访问私有内容
        friend void goodGay::visit();
    public:
        Building();
    public:
        string m_SittingRoom; //客厅
    private:
        string m_BedRoom;//卧室
    };
    Building::Building()
    {
        this->m_SittingRoom = "客厅";
        this->m_BedRoom = "卧室";
    }
    goodGay::goodGay()
    {
        building = new Building;
    }
    void goodGay::visit()
    {
        cout << "好基友正在访问" << building->m_SittingRoom << endl;
        cout << "好基友正在访问" << building->m_BedRoom << endl;
    }
    void goodGay::visit2()
    {
        cout << "好基友正在访问" << building->m_SittingRoom << endl;
        //cout << "好基友正在访问" << building->m_BedRoom << endl;
    }
    void test01()
    {
        goodGay  gg;
        gg.visit();
    }
    int main(){
  test01();
  system("pause");
  return 0;
  }
友元总结:成为其他类或函数的朋友,就可以访问其私有成员。 
操作符重载 C++中操作符就是一种函数。
加号运算符重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 class  Person  {public :	Person() {}; 	Person(int  a, int  b) 	{ 		this ->m_A = a; 		this ->m_B = b; 	} 	 	Person operator +(const  Person& p) { 		Person temp; 		temp.m_A = this ->m_A + p.m_A; 		temp.m_B = this ->m_B + p.m_B; 		return  temp; 	} public :	int  m_A; 	int  m_B; }; Person operator +(const  Person& p2, int  val) { 	Person temp; 	temp.m_A = p2.m_A + val; 	temp.m_B = p2.m_B + val; 	return  temp; } void  test ()  	Person p1 (10 , 10 )  ; 	Person p2 (20 , 20 )  ; 	 	Person p3 = p2 + p1;   	cout  << "mA:"  << p3.m_A << " mB:"  << p3.m_B << endl ; 	Person p4 = p3 + 10 ;  	cout  << "mA:"  << p4.m_A << " mB:"  << p4.m_B << endl ; } int  main ()  	test(); 	system("pause" ); 	return  0 ; } 
对于内置的数据类型的表达式的运算符不能被改变,如 int, double, float 但是自定义的是可以进行更改的。 
 
左移运算符 <<  重载 通常不会利用成员函数重载 << 运算符,因为无法实现 cout 在左侧 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class  Person  {	friend  ostream& operator <<(ostream& out, Person& p); public :	Person(int  a, int  b) 	{ 		this ->m_A = a; 		this ->m_B = b; 	} 	 	 	 private :	int  m_A; 	int  m_B; }; ostream& operator <<(ostream& cout , Person& p) { 	cout  << "a:"  << p.m_A << " b:"  << p.m_B; 	return  cout ; } void  test ()  	Person p1 (10 , 20 )  ; 	cout  << p1 << "hello world"  << endl ;  } int  main ()  	test(); 	system("pause" ); 	return  0 ; } 
递增运算符重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #include  <iostream>  using  namespace  std ;class  MyInteger  {	friend  ostream& operator <<(ostream& out, MyInteger myint); public :	MyInteger() { 		m_Num = 0 ; 	} 	 	MyInteger& operator ++() { 		 		m_Num++; 		 		return  *this ; 	} 	 	MyInteger& operator --() { 		 		m_Num--; 		 		return  *this ; 	} 	 	MyInteger operator ++(int ) { 		 		MyInteger temp = *this ;  		m_Num++; 		return  temp; 	} 	 	MyInteger operator --(int ) { 		 		MyInteger temp = *this ;  		m_Num--; 		return  temp; 	} private :	int  m_Num; }; ostream& operator <<(ostream& out, MyInteger myint) { 	out << myint.m_Num; 	return  out; } void  test01 ()  	MyInteger myInt; 	cout  << ++myInt << endl ; 	cout  << myInt << endl ; } void  test02 ()  	MyInteger myInt; 	cout  << myInt++ << endl ; 	cout  << myInt << endl ; } void  test03 ()  	MyInteger myInt; 	cout  << myInt-- << endl ; } void  test04 ()  	MyInteger myInt; 	cout  << --myInt << endl ; } int  main ()  	 	 	 	test04(); 	system("pause" ); 	return  0 ; } 
前置递增返回的是引用,后置递增返回的是值(由于提前 return 会结束当前函数,所以后置递增时,先用 temp 接受当前值,然后递增完,返回 temp 的值) 
赋值运算符(=)的重载 C++ 编译器至少给一个类添加4个函数
默认构造函数(无参数,函数体为空) 
默认析构函数(无参数,函数体为空) 
默认拷贝构造函数,对属性进行值拷贝 
赋值运算符 operator= ,对属性进行值拷贝 
 
问题所在:
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 class  Person { public :	Person(int  age) 	{ 		 		m_Age = new  int (age); 	} 	 	Person& operator =(Person &p) 	{ 		if  (m_Age != NULL ) 		{ 			delete  m_Age; 			m_Age = NULL ; 		} 		 		 		 		m_Age = new  int (*p.m_Age); 		 		return  *this ; 	} 	~Person() 	{ 		if  (m_Age != NULL ) 		{ 			delete  m_Age; 			m_Age = NULL ; 		} 	} 	 	int  *m_Age; }; void  test01 () 	Person p1 (18 )  ; 	Person p2 (20 )  ; 	Person p3 (30 )  ; 	p3 = p2 = p1;  	cout  << "p1的年龄为:"  << *p1.m_Age << endl ; 	cout  << "p2的年龄为:"  << *p2.m_Age << endl ; 	cout  << "p3的年龄为:"  << *p3.m_Age << endl ; } int  main ()  	test01(); 	 	 	 	 	 	 	 	system("pause" ); 	return  0 ; } 
关系运算符(==)重载 作用:重载关系运算符,可以让两个自定义类型对象进行对比操作 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 class  Person { public :	Person(string  name, int  age) 	{ 		this ->m_Name = name; 		this ->m_Age = age; 	}; 	bool  operator ==(Person & p) 	{ 		if  (this ->m_Name == p.m_Name && this ->m_Age == p.m_Age) 		{ 			return  true ; 		} 		else  		{ 			return  false ; 		} 	} 	bool  operator !=(Person & p) 	{ 		if  (this ->m_Name == p.m_Name && this ->m_Age == p.m_Age) 		{ 			return  false ; 		} 		else  		{ 			return  true ; 		} 	} 	string  m_Name; 	int  m_Age; }; void  test01 () 	 	 	Person a ("孙悟空" , 18 )  ; 	Person b ("孙悟空" , 18 )  ; 	if  (a == b) 	{ 		cout  << "a和b相等"  << endl ; 	} 	else  	{ 		cout  << "a和b不相等"  << endl ; 	} 	if  (a != b) 	{ 		cout  << "a和b不相等"  << endl ; 	} 	else  	{ 		cout  << "a和b相等"  << endl ; 	} } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
函数调用运算符 () 重载 
函数调用运算符()也可以重载 
由于重载后使用的方式非常像函数调用,因此称为仿函数 
仿函数没有固定写法,非常灵活 
 
即为对一个类的对象后使用()进行类似于函数式的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class  MyPrint { public :	void  operator () (string  text )  	 {		cout  << text  << endl ; 	} }; void  test01 () 	 	MyPrint myFunc; 	myFunc("hello world" ); } class  MyAdd { public :	int  operator () (int  v1, int  v2)  	 {		return  v1 + v2; 	} }; void  test02 () 	MyAdd add; 	int  ret = add(10 , 10 ); 	cout  << "ret = "  << ret << endl ; 	 	cout  << "MyAdd()(100,100) = "  << MyAdd()(100 , 100 ) << endl ; } int  main ()  	test01(); 	test02(); 	system("pause" ); 	return  0 ; } 
继承 语法: class   子类  :  继承方式  父类
子类:派生类
父类:基类
权限:公共、保护、私有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 class  Base1 { public :	int  m_A; protected :	int  m_B; private :	int  m_C; }; class  Son1  :public  Base1{ public :	void  func ()  	 {		m_A;  		m_B;  		 	} }; void  myClass () 	Son1 s1; 	s1.m_A;  } class  Base2 { public :	int  m_A; protected :	int  m_B; private :	int  m_C; }; class  Son2 :protected  Base2{ public :	void  func ()  	 {		m_A;  		m_B;  		 	} }; void  myClass2 () 	Son2 s; 	 } class  Base3 { public :	int  m_A; protected :	int  m_B; private :	int  m_C; }; class  Son3 :private  Base3{ public :	void  func ()  	 {		m_A;  		m_B;  		 	} }; class  GrandSon3  :public  Son3{ public :	void  func ()  	 {		 		 		 		 	} }; 
继承中的对象模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class  Base { public :	int  m_A; protected :	int  m_B; private :	int  m_C;  }; class  Son  :public  Base{ public :	int  m_D; }; void  test01 () 	cout  << "sizeof Son = "  << sizeof (Son) << endl ; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
 父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后访问不到 
继承中构造和析构顺序 
继承中同名成员的处理方式 
访问子类同名成员,直接访问 
访问父类同名成员,需要加作用域 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 class  Base  {public :	Base() 	{ 		m_A = 100 ; 	} 	void  func ()  	 {		cout  << "Base - func()调用"  << endl ; 	} 	void  func (int  a)  	 {		cout  << "Base - func(int a)调用"  << endl ; 	} public :	int  m_A; }; class  Son  :public  Base {public :	Son() 	{ 		m_A = 200 ; 	} 	 	 	void  func ()  	 {		cout  << "Son - func()调用"  << endl ; 	} public :	int  m_A; }; void  test01 () 	Son s; 	cout  << "Son下的m_A = "  << s.m_A << endl ; 	cout  << "Base下的m_A = "  << s.Base::m_A << endl ; 	s.func(); 	s.Base::func(); 	s.Base::func(10 ); } int  main ()  	test01(); 	system("pause" ); 	return  EXIT_SUCCESS; } 
//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数
//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
总结:想要访问父类中被重复的对象,添加作用域才可访问到。
继承同名静态成员处理方式 
访问子类同名成员,直接访问即可 
访问父类同名成员,需要天机啊作用域 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 class  Base  {public :	static  void  func ()  	 {		cout  << "Base - static void func()"  << endl ; 	} 	static  void  func (int  a)  	 {		cout  << "Base - static void func(int a)"  << endl ; 	} 	static  int  m_A; }; int  Base::m_A = 100 ;class  Son  :public  Base {public :	static  void  func ()  	 {		cout  << "Son - static void func()"  << endl ; 	} 	static  int  m_A; }; int  Son::m_A = 200 ;void  test01 () 	 	cout  << "通过对象访问: "  << endl ; 	Son s; 	cout  << "Son  下 m_A = "  << s.m_A << endl ; 	cout  << "Base 下 m_A = "  << s.Base::m_A << endl ; 	 	cout  << "通过类名访问: "  << endl ; 	cout  << "Son  下 m_A = "  << Son::m_A << endl ; 	cout  << "Base 下 m_A = "  << Son::Base::m_A << endl ; } void  test02 () 	 	cout  << "通过对象访问: "  << endl ; 	Son s; 	s.func(); 	s.Base::func(); 	cout  << "通过类名访问: "  << endl ; 	Son::func(); 	Son::Base::func(); 	 	Son::Base::func(100 ); } int  main ()  	 	test02(); 	system("pause" ); 	return  0 ; } 
多继承语法 C++ 允许一个类继承多个类
语法:  class 子类 :继承方式 父类1, 继承方式 父类2
多继承可能会引发父类中有同名成员出现,需要加作用域区分 
C++ 实际开发中不建议用多继承 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class  Base1  {public :	Base1() 	{ 		m_A = 100 ; 	} public :	int  m_A; }; class  Base2  {public :	Base2() 	{ 		m_A = 200 ;   	} public :	int  m_A; }; class  Son  :public  Base2, public  Base1{ public :	Son() 	{ 		m_C = 300 ; 		m_D = 400 ; 	} public :	int  m_C; 	int  m_D; }; void  test01 () 	Son s; 	cout  << "sizeof Son = "  << sizeof (s) << endl ; 	cout  << s.Base1::m_A << endl ; 	cout  << s.Base2::m_A << endl ; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
菱形继承问题的解决方式 菱形继承即,多继承了父类的相同同名属性,带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
在继承之前,添加关键字 virtual 变为虚继承
class Sheep : virtual public Animal{};
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class  Animal { public :	int  m_Age; }; class  Sheep  :virtual  public  Animal {};class  Tuo    :virtual  public  Animal {};class  SheepTuo  :public  Sheep, public  Tuo {};void  test01 () 	SheepTuo st; 	st.Sheep::m_Age = 100 ; 	st.Tuo::m_Age = 200 ; 	cout  << "st.Sheep::m_Age = "  << st.Sheep::m_Age << endl ; 	cout  << "st.Tuo::m_Age = "  <<  st.Tuo::m_Age << endl ; 	cout  << "st.m_Age = "  << st.m_Age << endl ; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
多态 
==动态多态: 派生类和虚函数实现运行时多态==
  函数地址在运行阶段才能确定,就是动态联编
 
静态多态和动态多态区别:
静态多态的函数地址早绑定  -  编译阶段确定函数地址
动态多态的函数地址晚绑定  -  运行阶段确定函数地址
(动态地址通过在父类函数中使用 virtual 来进行虚函数)
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 class  Animal { public :	 	 	virtual  void  speak ()  	 {		cout  << "动物在说话"  << endl ; 	} }; class  Cat  :public  Animal{ public :	void  speak ()  	 {		cout  << "小猫在说话"  << endl ; 	} }; class  Dog  :public  Animal{ public :	void  speak ()  	 {		cout  << "小狗在说话"  << endl ; 	} }; void  DoSpeak (Animal & animal) 	animal.speak(); } void  test01 () 	Cat cat; 	DoSpeak(cat); 	Dog dog; 	DoSpeak(dog); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
纯虚函数和抽象类 父类中的虚函数实现大多数时候是毫无意义的,通常都是利用子类中进行调用子类的重写的内容。
子类必须重写父类中的纯虚函数(Java接口同)
当类中有了纯虚函数,那么这个类就称为==抽象类==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class  Base { public :	 	 	 	 	virtual  void  func ()  0 ; }; class  Son  :public  Base{ public :	virtual  void  func ()  	 {		cout  << "func调用"  << endl ; 	}; }; void  test01 () 	Base * base = NULL ; 	 	base = new  Son; 	base->func(); 	delete  base; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
多态案例,制作饮品 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #include  <iostream>  using  namespace  std ;class  Abstractdrinking {public :    virtual  void  HeatWater ()  0 ;     virtual  void  Brew ()  0 ;     virtual  void  PourIn ()  0 ;     virtual  void  AddSup ()  0 ;     void  MakeDrinking ()          HeatWater();         Brew();         PourIn();         AddSup();     } }; class  Coffee :public  Abstractdrinking{    virtual  void  HeatWater ()         cout <<"Heat NongF Water." <<endl ;     }     virtual  void  Brew ()         cout <<"brew coffee." <<endl ;     }     virtual  void  PourIn ()         cout <<"Pour in NongF Water for Coffee." <<endl ;     }     virtual  void  AddSup ()         cout <<"Add support milk & sugar." <<endl ;     } }; class  Tea :public  Abstractdrinking{    virtual  void  HeatWater ()          cout <<"Heat Quan Water." <<endl ;     }     virtual  void  Brew ()          cout <<"brew tea." <<endl ;     }     virtual  void  PourIn ()          cout <<"Pour in Quan Water for tea." <<endl ;     }     virtual  void  AddSup ()          cout <<"Add support lemon." <<endl ;     } }; void  doWork (Abstractdrinking &abstractdrinking)     abstractdrinking.MakeDrinking(); } void  doWork (Abstractdrinking *abstractdrinking)     abstractdrinking->MakeDrinking();     delete  abstractdrinking;     abstractdrinking = nullptr ; } void  test01 ()     Abstractdrinking *coffee = new  Coffee();     doWork(*coffee);     delete  coffee;     coffee = nullptr ;     cout <<"---------------------------------" <<endl ;     Abstractdrinking *tea = new  Tea();     doWork(*tea);     delete  tea;     tea = nullptr ; } void  test02 ()     cout <<"doWork with test02 using point." <<endl ;     doWork(new  Coffee());     cout <<"---------------------------------" <<endl ;     doWork(new  Tea()); } int  main ()      test01();     cout <<"=================================" <<endl ;     test02();     return  0 ; } 
虚析构和纯虚析构 父类指针在析构的时候,不会调用子类中析构函数,导致子类如果有堆区属性,会出现内存泄漏的情况
利用虚析构可以解决父类指针释放子类对象时不干净的问题
virtual ~Animal(){cout<<"destruct"<<endl;}
==纯虚析构需要声明也需要实现==
虚析构和纯虚析构共性:
可以解决父类指针释放子类对象 
都需要有具体的函数实现 
 
虚析构和纯虚析构区别:
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名()=0
类名::~类名(){} (类内声明,类外实现)
总结:
    1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
    2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
3. 拥有纯虚析构函数的类也属于抽象类(抽象类无法实例化对象)
多态案例:电脑组装 案例描述: 
电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储)
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 #include <iostream>  using  namespace  std ;class  CPU { public :	 	virtual  void  calculate ()  0 ; }; class  VideoCard { public :	 	virtual  void  display ()  0 ; }; class  Memory { public :	 	virtual  void  storage ()  0 ; }; class  Computer { public :	Computer(CPU * cpu, VideoCard * vc, Memory * mem) 	{ 		m_cpu = cpu; 		m_vc = vc; 		m_mem = mem; 	} 	 	void  work ()  	 {		 		m_cpu->calculate(); 		m_vc->display (); 		m_mem->storage(); 	} 	 	~Computer() 	{ 		 		if  (m_cpu != NULL ) 		{ 			delete  m_cpu; 			m_cpu = NULL ; 		} 		 		if  (m_vc != NULL ) 		{ 			delete  m_vc; 			m_vc = NULL ; 		} 		 		if  (m_mem != NULL ) 		{ 			delete  m_mem; 			m_mem = NULL ; 		} 	} private :	CPU * m_cpu;  	VideoCard * m_vc;  	Memory * m_mem;  }; class  IntelCPU  :public  CPU{ public :	virtual  void  calculate ()  	 {		cout  << "Intel的CPU开始计算了!"  << endl ; 	} }; class  IntelVideoCard  :public  VideoCard{ public :	virtual  void  display ()  	 {		cout  << "Intel的显卡开始显示了!"  << endl ; 	} }; class  IntelMemory  :public  Memory{ public :	virtual  void  storage ()  	 {		cout  << "Intel的内存条开始存储了!"  << endl ; 	} }; class  LenovoCPU  :public  CPU{ public :	virtual  void  calculate ()  	 {		cout  << "Lenovo的CPU开始计算了!"  << endl ; 	} }; class  LenovoVideoCard  :public  VideoCard{ public :	virtual  void  display ()  	 {		cout  << "Lenovo的显卡开始显示了!"  << endl ; 	} }; class  LenovoMemory  :public  Memory{ public :	virtual  void  storage ()  	 {		cout  << "Lenovo的内存条开始存储了!"  << endl ; 	} }; void  test01 () 	 	CPU * intelCpu = new  IntelCPU; 	VideoCard * intelCard = new  IntelVideoCard; 	Memory * intelMem = new  IntelMemory; 	cout  << "第一台电脑开始工作:"  << endl ; 	 	Computer * computer1 = new  Computer(intelCpu, intelCard, intelMem); 	computer1->work(); 	delete  computer1; 	cout  << "-----------------------"  << endl ; 	cout  << "第二台电脑开始工作:"  << endl ; 	 	Computer * computer2 = new  Computer(new  LenovoCPU, new  LenovoVideoCard, new  LenovoMemory);; 	computer2->work(); 	delete  computer2; 	cout  << "-----------------------"  << endl ; 	cout  << "第三台电脑开始工作:"  << endl ; 	 	Computer * computer3 = new  Computer(new  LenovoCPU, new  IntelVideoCard, new  LenovoMemory);; 	computer3->work(); 	delete  computer3; } 
文件操作 文件类型:
文本文件 - 以文本的 ASCII 码形式存储在计算机中 
以二进制形式存储在计算机中,一般用户不能直接读懂 
 
==包含头文件 #include\== 
操作文件的三大类:
ofstream:写操作 
ifstream:读操作 
fstream:读写操作 
 
打开方式 
解释 
 
 
ios::in 
为读文件而打开文件 
 
ios::out 
为写文件而打开文件 
 
ios::ate 
初始位置:文件尾 
 
ios::app 
追加方式写文件 
 
ios::trunc 
如果文件存在先删除,再创建 
 
ios::binary 
二进制方式 
 
 
 
利用 | 操作符可以设置二进制方式写文件 ios::binary | ios::out
写文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include  <fstream>  void  test01 () 	ofstream ofs; 	ofs.open ("test.txt" , ios::out); 	ofs << "姓名:张三"  << endl ; 	ofs << "性别:男"  << endl ; 	ofs << "年龄:18"  << endl ; 	ofs.close (); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
文件操作必须包含头文件 fstream 
读文件可以利用 ofstream  ,或者fstream类 
打开文件时候需要指定操作文件的路径,以及打开方式 
利用<<可以向文件中写数据 
操作完毕,要关闭文件 
 
读文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include  <fstream>  #include  <string>  void  test01 () 	ifstream ifs; 	ifs.open ("test.txt" , ios::in); 	if  (!ifs.is_open()) 	{ 		cout  << "文件打开失败"  << endl ; 		return ; 	} 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	                   	char  c; 	while  ((c = ifs.get ()) != EOF) 	{ 		cout  << c; 	} 	ifs.close (); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
is_open 可以判断文件是否打开成功
二进制文件 写文件 
函数原型:
ostream& write(const char * buffer, int len);
buffer 指向内存中一段存储空间,len 是读写的字节数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include  <fstream>  #include  <string>  class  Person { public :	char  m_Name[64 ]; 	int  m_Age; }; void  test01 () 	 	 	ofstream ofs ("person.txt" , ios::out | ios::binary)  ; 	 	 	Person p = {"张三"   , 18 }; 	    	ofs.write ((const  char  *)&p, sizeof (p)); 	 	ofs.close (); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
读文件 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include  <fstream>  #include  <string>  class  Person { public :	char  m_Name[64 ]; 	int  m_Age; }; void  test01 () 	ifstream ifs ("person.txt" , ios::in | ios::binary)  ; 	if  (!ifs.is_open()) 	{ 		cout  << "文件打开失败"  << endl ; 	} 	Person p; 	ifs.read ((char  *)&p, sizeof (p)); 	cout  << "姓名: "  << p.m_Name << " 年龄: "  << p.m_Age << endl ; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
C++ 泛型概念(模板) 模板特点:
模板不可以直接使用,它作为一个框架
模板不是万能通用的
 
函数模板和类模板 
函数模板语法 函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型 来代表。
语法: 
1 2 template <typename  T>函数声明或定义 
解释: 
template  —-  声明创建模板
typename  —- 表面其后面的符号是一种数据类型,可以用class代替(即为类模板)
T    —-   通用的数据类型,名称可以替换,通常为大写字母
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 void  swapInt (int & a, int & b)  	int  temp = a; 	a = b; 	b = temp; } void  swapDouble (double & a, double & b)  	double  temp = a; 	a = b; 	b = temp; } template <typename  T>void  mySwap (T& a, T& b) 	T temp = a; 	a = b; 	b = temp; } void  test01 () 	int  a = 10 ; 	int  b = 20 ; 	 	 	 	mySwap(a, b); 	 	mySwap<int >(a, b); 	cout  << "a = "  << a << endl ; 	cout  << "b = "  << b << endl ; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
总结:
函数模板注意事项 注意事项:
自动类型推导,必须推导出一致的数据类型T,才可以使用 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 template <class  T >void  mySwap (T & a , T & b ){ 	T temp = a; 	a = b; 	b = temp; } void  test01 () 	int  a = 10 ; 	int  b = 20 ; 	char  c = 'c' ; 	mySwap(a, b);  	 } template <class  T >void  func (){ 	cout  << "func 调用"  << endl ; } void  test02 () 	 	func<int >();  } int  main ()  	test01(); 	test02(); 	system("pause" ); 	return  0 ; } 
使用模板时必须确定出通用数据类型 T ,并且能够推导出一致的类型 
函数模板案例:选择排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 template <typename  T>void  mySwap (T &a, T &b)     T temp = a;     a = b;     b = temp; } template <class  T >void  mySort (T  arr [], T  len ){     for (int  i = 0 ; i < len; i++)     {         int  max  = i;         for (int  j = i+1 ; j < len; j++){             if (arr[j] > max ){                 max  = j;             }         }         if (max  != i){             mySwap(arr[max ], arr[i]);         }     } } template <typename  T>void  printArray (T arr[], int  len)  	for  (int  i = 0 ; i < len; i++) { 		cout  << arr[i] << " " ; 	} 	cout  << endl ; } void  test01 () 	 	char  charArr[] = "bdcfeagh" ; 	 	 	mySort(charArr, sizeof (charArr)/sizeof (char ) ); 	 	printArray(charArr, sizeof (charArr)/sizeof (char )); } void  test02 () 	 	int  intArr[] = { 7 , 5 , 8 , 1 , 3 , 9 , 2 , 4 , 6  }; 	int  num = sizeof (intArr) / sizeof (int ); 	mySort(intArr, num); 	printArray(intArr, num); } int  main ()  	test01(); 	test02(); 	 	return  0 ; } 
普通函数与函数模板的区别 普通函数与函数模板区别: 
普通函数调用时可以发生自动类型转换(隐式类型转换) 
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换 
如果利用显示指定类型的方式,可以发生隐式类型转换  
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 int  myAdd01 (int  a, int  b) 	return  a + b; } template <class  T >T  myAdd02 (T  a , T  b ){ 	return  a + b; } void  test01 () 	int  a = 10 ; 	int  b = 20 ; 	char  c = 'c' ; 	cout  << myAdd01(a, c) << endl ;  	 	myAdd02<int >(a, c);  } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
模板的局限性 局限性在于:对于模板中的 T 这个数据类型来说,有些时候它推导不出来 T 属于什么类型 ,这是最大的局限性
例如: 
1 2 3 4 5 template <class  T >void  f (T  a , T  b ){    	a = b;    } 
在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了
再例如:
1 2 3 4 5 template <class  T >void  f (T  a , T  b ){    	if (a > b) { ... }    } 
在上述代码中,如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行
因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型 提供具体化的模板 
具体化的模板 
语法:
1 2 3 template <> bool  myCompare (Person &p1, Person &p2) 
对比于:
1 2 3 4 template  <class  T >void  myTest (int  a , int  b ){    ... } 
类模板 注意:
类模板只能用显示指定类型方式 
类模板中模板参数列表可以有默认参数 
 
语法:
1 2 template <typename  T>class  className {
在类模板中,声明模板 template 后面加类 
类模板与函数模板区别 
类模板没有自动类型推导的使用方式 
类模板使用只能用显示指定类型方式 
类模板在模板参数列表中有默认参数 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include  <string>  template <class  NameType , class  AgeType  = int > 	//设置的默认参数 int class  Person { public :	Person(NameType name, AgeType age) 	{ 		this ->mName = name; 		this ->mAge = age; 	} 	void  showPerson ()  	 {		cout  << "name: "  << this ->mName << " age: "  << this ->mAge << endl ; 	} public :	NameType mName; 	AgeType mAge; }; void  test01 () 	 	Person <string  ,int >p("孙悟空" , 1000 );  	p.showPerson(); } void  test02 () 	Person <string > p("猪八戒" , 999 );  	p.showPerson(); } int  main ()  	test01(); 	test02(); 	system("pause" ); 	return  0 ; } 
类模板中成员函数创建时机 
普通类中的成员函数一开始就可以创建 
类模板中的成员函数在调用时才创建 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class  Person1 { public :	void  showPerson1 ()  	 {		cout  << "Person1 show"  << endl ; 	} }; class  Person2 { public :	void  showPerson2 ()  	 {		cout  << "Person2 show"  << endl ; 	} }; template <class  T >class  MyClass { public :	T obj; 	 	void  fun1 ()   	void  fun2 ()   }; void  test01 () 	MyClass<Person1> m; 	m.fun1(); 	 } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
类模板对象作函数参数 
指定传入的类型   —- 直接显示对象的数据类型 参数模板化           —- 将对象中的参数变为模板进行传递 
整个类模板化       —- 将这个对象类型 模板化进行传递 
 
其中指定数据类型是使用最广泛的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include  <string>  template <class  NameType , class  AgeType  = int >class  Person { public :	Person(NameType name, AgeType age) 	{ 		this ->mName = name; 		this ->mAge = age; 	} 	void  showPerson ()  	 {		cout  << "name: "  << this ->mName << " age: "  << this ->mAge << endl ; 	} public :	NameType mName; 	AgeType mAge; }; void  printPerson1 (Person<string , int > &p) 	p.showPerson(); } void  test01 () 	Person <string , int  >p("孙悟空" , 100 ); 	printPerson1(p); } template  <class  T1 , class  T2 >void  printPerson2 (Person <T1, T2>&p ){ 	p.showPerson(); 	cout  << "T1的类型为: "  << typeid (T1).name() << endl ; 	cout  << "T2的类型为: "  << typeid (T2).name() << endl ; } void  test02 () 	Person <string , int  >p("猪八戒" , 90 ); 	printPerson2(p); } template <class  T >void  printPerson3 (T  & p ){ 	cout  << "T的类型为: "  << typeid (T).name() << endl ; 	p.showPerson(); } void  test03 () 	Person <string , int  >p("唐僧" , 30 ); 	printPerson3(p); } int  main ()  	test01(); 	test02(); 	test03(); 	system("pause" ); 	return  0 ; } 
类模板与继承 
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中 T 的类型 
如果不指定,编译器无法给子类分配内存 
如果想灵活指定出父类中T的类型,子类也需变为类模板 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 template <class  T >class  Base { 	T m; }; class  Son  :public  Base<int > { }; void  test01 () 	Son c; } template <class  T1 , class  T2 >class  Son2  :public  Base<T2>{ public :	Son2() 	{ 		cout  << typeid (T1).name() << endl ; 		cout  << typeid (T2).name() << endl ; 	} }; void  test02 () 	Son2<int , char > child1; } int  main ()  	test01(); 	test02(); 	system("pause" ); 	return  0 ; } 
类模板成员函数与类外实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include  <string>  template <class  T1 , class  T2 >class  Person  {public :	 	Person(T1 name, T2 age); 	void  showPerson ()  public :	T1 m_Name; 	T2 m_Age; }; template <class  T1 , class  T2 >Person <T1, T2>:	this ->m_Name = name; 	this ->m_Age = age; } template <class  T1 , class  T2 >void  Person <T1, T2>:	cout  << "姓名: "  << this ->m_Name << " 年龄:"  << this ->m_Age << endl ; } void  test01 () 	Person<string, int> p("Tom", 20); 	p.showPerson(); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
类外实现需要添加模板参数列表
类模板分文件编写 #pragma once防止头文件重复编译,一般写在.h头文件中
问题:
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到 
 
解决:
解决方式1:直接包含在.cpp源文件 
解决方式2:只将类模板的声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制 
 
person.hpp 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #pragma  once #include  <iostream>  using  namespace  std ;#include  <string>  template <class  T1 , class  T2 >class  Person  {public :	Person(T1 name, T2 age); 	void  showPerson ()  public :	T1 m_Name; 	T2 m_Age; }; template <class  T1 , class  T2 >Person <T1, T2>:	this ->m_Name = name; 	this ->m_Age = age; } template <class  T1 , class  T2 >void  Person <T1, T2>:	cout  << "姓名: "  << this ->m_Name << " 年龄:"  << this ->m_Age << endl ; } 
main.cpp 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream>  using  namespace  std ;#include  "person.cpp"   #include  "person.hpp"  void  test01 () 	Person<string, int> p("Tom", 10); 	p.showPerson(); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
类模板案例 
可以对内置数据类型以及自定义数据类型的数据进行存储 
将数组中的数据存储到堆区 
构造函数中可以传入数组的容量 
提供对应的拷贝构造函数以及operator=防止浅拷贝问题 
提供尾插法和尾删法对数组中的数据进行增加和删除 
可以通过下标的方式访问数组中的元素 
可以获取数组中当前元素个数和数组的容量 
 
myArr.hpp 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #pragma  once #include  <iostream>  using  namespace  std ;template <class  T >class  MyArray { public :	 	MyArray(int  capacity) 	{ 		this ->m_Capacity = capacity; 		this ->m_Size = 0 ; 		pAddress = new  T[this ->m_Capacity]; 	} 	 	MyArray(const  MyArray & arr) 	{ 		this ->m_Capacity = arr.m_Capacity; 		this ->m_Size = arr.m_Size; 		this ->pAddress = new  T[this ->m_Capacity]; 		for  (int  i = 0 ; i < this ->m_Size; i++) 		{ 			 			 			this ->pAddress[i] = arr.pAddress[i]; 		} 	} 	 	MyArray& operator =(const  MyArray& myarray) { 		if  (this ->pAddress != NULL ) { 			delete [] this ->pAddress; 			this ->m_Capacity = 0 ; 			this ->m_Size = 0 ; 		} 		this ->m_Capacity = myarray.m_Capacity; 		this ->m_Size = myarray.m_Size; 		this ->pAddress = new  T[this ->m_Capacity]; 		for  (int  i = 0 ; i < this ->m_Size; i++) { 			this ->pAddress[i] = myarray[i]; 		} 		return  *this ; 	} 	 	T& operator  [](int  index) 	{ 		return  this ->pAddress[index];  	} 	 	void  Push_back (const  T & val)  	 {		if  (this ->m_Capacity == this ->m_Size) 		{ 			return ; 		} 		this ->pAddress[this ->m_Size] = val; 		this ->m_Size++; 	} 	 	void  Pop_back ()  	 {		if  (this ->m_Size == 0 ) 		{ 			return ; 		} 		this ->m_Size--; 	} 	 	int  getCapacity ()  	 {		return  this ->m_Capacity; 	} 	 	int 	getSize ()  	 {		return  this ->m_Size; 	} 	 	~MyArray() 	{ 		if  (this ->pAddress != NULL ) 		{ 			delete [] this ->pAddress; 			this ->pAddress = NULL ; 			this ->m_Capacity = 0 ; 			this ->m_Size = 0 ; 		} 	} private :	T * pAddress;   	int  m_Capacity;  	int  m_Size;    }; 
main.cpp 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #include  "myArr.hpp"  #include  <string>  void  printIntArray (MyArray<int >& arr)  	for  (int  i = 0 ; i < arr.getSize(); i++) { 		cout  << arr[i] << " " ; 	} 	cout  << endl ; } void  test01 () 	MyArray<int > array1 (10 )  ; 	for  (int  i = 0 ; i < 10 ; i++) 	{ 		array1.Push_back(i); 	} 	cout  << "array1打印输出:"  << endl ; 	printIntArray(array1); 	cout  << "array1的大小:"  << array1.getSize() << endl ; 	cout  << "array1的容量:"  << array1.getCapacity() << endl ; 	cout  << "--------------------------"  << endl ; 	MyArray<int > array2 (array1)  ; 	array2.Pop_back(); 	cout  << "array2打印输出:"  << endl ; 	printIntArray(array2); 	cout  << "array2的大小:"  << array2.getSize() << endl ; 	cout  << "array2的容量:"  << array2.getCapacity() << endl ; } class  Person  {public :	Person() {} 		Person(string  name, int  age) { 		this ->m_Name = name; 		this ->m_Age = age; 	} public :	string  m_Name; 	int  m_Age; }; void  printPersonArray (MyArray<Person>& personArr) 	for  (int  i = 0 ; i < personArr.getSize(); i++) { 		cout  << "姓名:"  << personArr[i].m_Name << " 年龄: "  << personArr[i].m_Age << endl ; 	} } void  test02 () 	 	MyArray<Person> pArray (10 )  ; 	Person p1 ("孙悟空" , 30 )  ; 	Person p2 ("韩信" , 20 )  ; 	Person p3 ("妲己" , 18 )  ; 	Person p4 ("王昭君" , 15 )  ; 	Person p5 ("赵云" , 24 )  ; 	 	pArray.Push_back(p1); 	pArray.Push_back(p2); 	pArray.Push_back(p3); 	pArray.Push_back(p4); 	pArray.Push_back(p5); 	printPersonArray(pArray); 	cout  << "pArray的大小:"  << pArray.getSize() << endl ; 	cout  << "pArray的容量:"  << pArray.getCapacity() << endl ; } int  main ()  	 	test02(); 	system("pause" ); 	return  0 ; } 
STL 
STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器 
容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。 
算法:各种常用的算法,如sort、find、copy、for_each等 
迭代器:扮演了容器与算法之间的胶合剂。 
仿函数:行为类似函数,可作为算法的某种策略。 
适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。 
空间配置器:负责空间的配置与管理。 
 
容器:置物之所也,常用数据结构:数组,链表,树,栈,队列,集合,映射表
序列式容器 :强调值的排序,序列式容器中的每个元素均有固定的位置。
**关联式容器**:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法:质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
迭代器:提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
 
vector 通过迭代器访问容器 vector 中的数据
vector<int>::iterator itBegin = v.begin(); 起始迭代器,指向容器中第一个元素
vector<int>::iterator itBegin = v.end(); 结束迭代器,指向容器中最后一个元素的下一个位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 vector <int > v;v.push_back(10 ); v.push_back(11 ); v.push_back(12 ); vector <int >::iterator itBegin = v.begin ();vector <int >::iterator itEnd = v.end ();while (itBegin != itEnd){    cout  << *itBegin++<<endl ; } for (vector <int >::iterator it = v.begin (); it != v.end ; it++){    cout <<*it<<endl ; } void  myPrint (int  val)     cout << val <<endl ; } for_each(v.begin (), v.end (), myPrint); 
vector容器中存放自定义数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include  <vector>  #include  <string>  class  Person  {public :	Person(string  name, int  age) { 		mName = name; 		mAge = age; 	} public :	string  mName; 	int  mAge; }; void  test01 ()  	vector <Person> v; 	 	Person p1 ("aaa" , 10 )  ; 	Person p2 ("bbb" , 20 )  ; 	Person p3 ("ccc" , 30 )  ; 	Person p4 ("ddd" , 40 )  ; 	Person p5 ("eee" , 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  << "Name:"  << (*it).mName << " Age:"  << (*it).mAge << endl ; 	} } void  test02 ()  	vector <Person*> v; 	 	Person p1 ("aaa" , 10 )  ; 	Person p2 ("bbb" , 20 )  ; 	Person p3 ("ccc" , 30 )  ; 	Person p4 ("ddd" , 40 )  ; 	Person p5 ("eee" , 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++) { 		Person * p = (*it); 		cout  << "Name:"  << p->mName << " Age:"  << (*it)->mAge << endl ; 	} } int  main ()  	test01(); 	test02(); 	system("pause" ); 	return  0 ; } 
vector 容器嵌套容器 1 2 3 4 5 6 7 8 9 10 11 12 13 vector <vector <int >> v;vector <int > v1;vector <int > v2;vector <int > v3;vector <int > v4;for ( vector <vector <int >>::iterator it = v.begin (); it != v.end (); it++ ){    for (vector <int >::iterator vit = (*it).begin (); vit != (*it).end (); (*it)++ ){         cout << *vit <<endl ;     } } 
插入和删除 功能描述: 
函数原型: 
push_back(ele);                                         //尾部插入元素elepop_back();                                                //删除最后一个元素insert(const_iterator pos, ele);        //迭代器指向位置pos插入元素eleinsert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素eleerase(const_iterator pos);                     //删除迭代器指向的元素erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素clear();                                                        //删除容器中所有元素 
swap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include  <iostream>  #include  <vector>  #include <algorithm>  using  namespace  std ;void  myPrint (int  val)     cout << val <<endl ; } int  main ()     vector <int > v1 = {1 ,2 ,3 ,4 ,5 };     vector <int > v2 = {6 ,7 ,8 ,9 ,10 };     for_each(v1.begin (), v1.end (), myPrint );     for_each(v2.begin (), v2.end (), myPrint ); 	v1.swap(v2);     for_each(v1.begin (), v1.end (), myPrint );     for_each(v2.begin (), v2.end (), myPrint );     return  0 ; } 
==swap 可以用来进行一个收缩内存的效果:vector\(v).swap(v);== 
利用 reserve 预留空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include  <vector>  void  test01 () 	vector <int > v; 	 	v.reserve(100000 ); 	int  num = 0 ; 	int * p = NULL ; 	for  (int  i = 0 ; i < 100000 ; i++) { 		v.push_back(i); 		if  (p != &v[0 ]) { 			p = &v[0 ]; 			num++; 		} 	} 	cout  << "num:"  << num << endl ; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
String String 的本质是一个类 
string 与 char * 的区别:
char *  是一个指针 
string 是一个类,类内部封装了 char * ,管理整个字符串,是一个 char * 型的容器 
 
构造函数:
string();                          //创建一个空的字符串 例如: string str;string(const char* s);            //使用字符串s初始化string(const string& str);    //使用一个string对象初始化另一个string对象string(int n, char c);           //使用n个字符c初始化 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include  <string>  void  test01 () 	string  s1;  	cout  << "str1 = "  << s1 << endl ; 	const  char * str = "hello world" ; 	string  s2 (str)  	cout  << "str2 = "  << s2 << endl ; 	string  s3 (s2)  	cout  << "str3 = "  << s3 << endl ; 	string  s4 (10 , 'a' )  	cout  << "str3 = "  << s3 << endl ; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
string赋值操作 功能描述:
赋值的函数原型:
string& operator=(const char* s);             //char*类型字符串 赋值给当前的字符串string& operator=(const string &s);         //把字符串s赋给当前的字符串string& operator=(char c);                          //字符赋值给当前的字符串string& assign(const char *s);                  //把字符串s赋给当前的字符串string& assign(const char *s, int n);     //把字符串s的前n个字符赋给当前的字符串string& assign(const string &s);              //把字符串s赋给当前字符串string& assign(int n, char c);                  //用n个字符c赋给当前字符串 
string字符串拼接 功能描述: 
函数原型: 
string& operator+=(const char* str);                   //重载+=操作符string& operator+=(const char c);                         //重载+=操作符string& operator+=(const string& str);                //重载+=操作符string& append(const char *s);                               //把字符串s连接到当前字符串结尾string& append(const char *s, int n);                 //把字符串s的前n个字符连接到当前字符串结尾string& append(const string &s);                           //同operator+=(const string& str)string& append(const string &s, int pos, int n);//字符串s中从pos开始的n个字符连接到字符串结尾 
string查找和替换 功能描述: 
查找:查找指定字符串是否存在 
替换:在指定的位置替换字符串 
 
函数原型: 
int find(const string& str, int pos = 0) const;              //查找str第一次出现位置,从pos开始查找int find(const char* s, int pos = 0) const;                     //查找s第一次出现位置,从pos开始查找int find(const char* s, int pos, int n) const;               //从pos位置查找s的前n个字符第一次位置int find(const char c, int pos = 0) const;                       //查找字符c第一次出现位置int rfind(const string& str, int pos = npos) const;      //查找str最后一次位置,从pos开始查找int rfind(const char* s, int pos = npos) const;              //查找s最后一次出现位置,从pos开始查找int rfind(const char* s, int pos, int n) const;              //从pos查找s的前n个字符最后一次位置int rfind(const char c, int pos = 0) const;                      //查找字符c最后一次出现位置string& replace(int pos, int n, const string& str);       //替换从pos开始n个字符为字符串strstring& replace(int pos, int n,const char* s);                 //替换从pos开始的n个字符为字符串s 
string字符串比较 功能描述: 
比较方式: 
= 返回   0
> 返回   1
< 返回  -1
函数原型: 
int compare(const string &s) const;  //与字符串s比较int compare(const char *s) const;      //与字符串s比较 
string字符存取 string中单个字符存取方式有两种
char& operator[](int n);     //通过[]方式取字符char& at(int n);                    //通过at方法获取字符 
string插入和删除 功能描述: 
函数原型: 
string& insert(int pos, const char* s);                //插入字符串string& insert(int pos, const string& str);        //插入字符串string& insert(int pos, int n, char c);                //在指定位置插入n个字符cstring& erase(int pos, int n = npos);                    //删除从Pos开始的n个字符 
string子串 功能描述: 
函数原型: 
string substr(int pos = 0, int n = npos) const;   //返回由pos开始的n个字符组成的字符串 
Deque容器 功能:双端数组,可以对头端进行插入删除操作 
deque 与 vector 的区别: 
vector 对于头部的插入删除效率低,数据量越大,效率越低 
对于头部的插入删除速度会比 vector 快 
vector 访问元素时的速度会比 deque 快 
 
deque 内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真是数据,中控器维护的是每个缓冲区的地址,使得使用 deque 时像一片连续的内存空间
deque 构造函数 函数原型: 
deque<T> deqT;                      //默认构造形式deque(beg, end);                  //构造函数将[beg, end)区间中的元素拷贝给本身。deque(n, elem);                    //构造函数将n个elem拷贝给本身。deque(const deque &deq);   //拷贝构造函数 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include  <deque>  void  printDeque (const  deque <int >& d) 	for  (deque <int >::const_iterator it = d.begin (); it != d.end (); it++) { 		cout  << *it << " " ; 	} 	cout  << endl ; } void  test01 ()  	deque <int > d1;  	for  (int  i = 0 ; i < 10 ; i++) 	{ 		d1.push_back(i); 	} 	printDeque(d1); 	deque <int > d2 (d1.begin (),d1.end ())  	printDeque(d2); 	deque <int >d3(10 ,100 ); 	printDeque(d3); 	deque <int >d4 = d3; 	printDeque(d4); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
deque 大小操作 deque 无容量限制,不像 vector 有限制,所以没有 .capacity 接口
函数原型: 
deque.empty();                       //判断容器是否为空
deque.size();                         //返回容器中元素的个数
deque.resize(num);                //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
                                           //如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem);     //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
                                                       //如果容器变短,则末尾超出容器长度的元素被删除。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include  <deque>  void  printDeque (const  deque <int >& d) 	for  (deque <int >::const_iterator it = d.begin (); it != d.end (); it++) { 		cout  << *it << " " ; 	} 	cout  << endl ; } void  test01 () 	deque <int > d1; 	for  (int  i = 0 ; i < 10 ; i++) 	{ 		d1.push_back(i); 	} 	printDeque(d1); 	 	if  (d1.empty()) { 		cout  << "d1为空!"  << endl ; 	} 	else  { 		cout  << "d1不为空!"  << endl ; 		 		cout  << "d1的大小为:"  << d1.size () << endl ; 	} 	 	d1.resize(15 , 1 ); 	printDeque(d1); 	d1.resize(5 ); 	printDeque(d1); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
deque 插入和删除 函数原型: 
两端插入操作:
push_back(elem);          //在容器尾部添加一个数据push_front(elem);        //在容器头部插入一个数据pop_back();                   //删除容器最后一个数据pop_front();                 //删除容器第一个数据 
指定位置操作:
insert(pos,elem);         //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);     //在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end);    //在pos位置插入[beg,end)区间的数据,无返回值。
clear();                           //清空容器的所有数据
erase(beg,end);             //删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos);                    //删除pos位置的数据,返回下一个数据的位置。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 #include  <deque>  void  printDeque (const  deque <int >& d) 	for  (deque <int >::const_iterator it = d.begin (); it != d.end (); it++) { 		cout  << *it << " " ; 	} 	cout  << endl ; } void  test01 () 	deque <int > d; 	 	d.push_back(10 ); 	d.push_back(20 ); 	 	d.push_front(100 ); 	d.push_front(200 ); 	printDeque(d); 	 	d.pop_back(); 	 	d.pop_front(); 	printDeque(d); } void  test02 () 	deque <int > d; 	d.push_back(10 ); 	d.push_back(20 ); 	d.push_front(100 ); 	d.push_front(200 ); 	printDeque(d); 	d.insert(d.begin (), 1000 ); 	printDeque(d); 	d.insert(d.begin (), 2 ,10000 ); 	printDeque(d); 	deque <int >d2; 	d2.push_back(1 ); 	d2.push_back(2 ); 	d2.push_back(3 ); 	d.insert(d.begin (), d2.begin (), d2.end ()); 	printDeque(d); } void  test03 () 	deque <int > d; 	d.push_back(10 ); 	d.push_back(20 ); 	d.push_front(100 ); 	d.push_front(200 ); 	printDeque(d); 	d.erase(d.begin ()); 	printDeque(d); 	d.erase(d.begin (), d.end ()); 	d.clear (); 	printDeque(d); } int  main ()  	 	     test03(); 	system("pause" ); 	return  0 ; } 
 总结:
尾插 push_back( ) 
尾删 pop_back( ) 
头插 push_front( ) 
头删 pop_front( ) 
 
deque 容器数据存取 函数原型: 
at(int idx);     //返回索引idx所指的数据operator[];      //返回索引idx所指的数据front();            //返回容器中第一个数据元素back();              //返回容器中最后一个数据元素 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include  <deque>  void  printDeque (const  deque <int >& d) 	for  (deque <int >::const_iterator it = d.begin (); it != d.end (); it++) { 		cout  << *it << " " ; 	} 	cout  << endl ; } void  test01 () 	deque <int > d; 	d.push_back(10 ); 	d.push_back(20 ); 	d.push_front(100 ); 	d.push_front(200 ); 	for  (int  i = 0 ; i < d.size (); i++) { 		cout  << d[i] << " " ; 	} 	cout  << endl ; 	for  (int  i = 0 ; i < d.size (); i++) { 		cout  << d.at(i) << " " ; 	} 	cout  << endl ; 	cout  << "front:"  << d.front() << endl ; 	cout  << "back:"  << d.back() << endl ; } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
deque 排序 算法 
利用 sort 算法进行对 deque 的排序
sort 算法也可以对 vector 等进行排序
sort(iterator begin, iterator end) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include  <iostream>  #include  <deque>  #include  <algorithm>  using  namespace  std ;void  printDeque (const  deque <int > &d)   for (deque <int >::const_iterator it = d.begin (); it != d.end (); it++ ){     cout << *it <<" " ;   }   cout  <<endl ; } void  test01 ()   deque <int > d;   d.push_back(30 );   d.push_back(40 );   d.push_back(100 );   d.push_front(200 );   d.push_front(300 );   printDeque(d);   sort(d.begin (), d.end ());   cout <<"after sort" <<endl ;   printDeque(d); } int  main ()   test01();   return  0 ; } 
rand 随机数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include  <iostream>  #include  <ctime>  using  namespace  std ;int  main () 	 	unsigned  int  seed; 	seed = time(0 ); 	srand(seed); 	int  first = rand() % 41  + 10 ; 	cout  << first <<endl ; 	int  second = rand() % 41  + 10 ; 	cout  << second <<endl ; 	return  0 ; } 
stack 构造函数:
stack<T> stk;                                 //stack采用模板类实现, stack对象的默认构造形式stack(const stack &stk);            //拷贝构造函数 
赋值操作:
stack& operator=(const stack &stk);           //重载等号操作符 
数据存取:
push(elem);      //向栈顶添加元素pop();                //从栈顶移除第一个元素top();                //返回栈顶元素 
大小操作:
empty();            //判断堆栈是否为空size();              //返回栈的大小 
queue 构造函数:
queue<T> que;                                 //queue采用模板类实现,queue对象的默认构造形式queue(const queue &que);            //拷贝构造函数 
赋值操作:
queue& operator=(const queue &que);           //重载等号操作符 
数据存取:
push(elem);                             //往队尾添加元素pop();                                      //从队头移除第一个元素back();                                    //返回最后一个元素front();                                  //返回第一个元素 
大小操作:
empty();            //判断堆栈是否为空size();              //返回栈的大小 
list 功能描述: 
函数原型: 
list<T> lst;                               //list采用采用模板类实现,对象的默认构造形式:list(beg,end);                           //构造函数将[beg, end)区间中的元素拷贝给本身。list(n,elem);                             //构造函数将n个elem拷贝给本身。list(const list &lst);            //拷贝构造函数。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include  <list>  void  printList (const  list <int >& L)  	for  (list <int >::const_iterator it = L.begin (); it != L.end (); it++) { 		cout  << *it << " " ; 	} 	cout  << endl ; } void  test01 () 	list <int >L1; 	L1.push_back(10 ); 	L1.push_back(20 ); 	L1.push_back(30 ); 	L1.push_back(40 ); 	printList(L1); 	list <int >L2(L1.begin (),L1.end ()); 	printList(L2); 	list <int >L3(L2); 	printList(L3); 	list <int >L4(10 , 1000 ); 	printList(L4); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
list 成员函数
list 数据存取 功能描述: 
函数原型: 
front();        //返回第一个元素。back();         //返回最后一个元素。 
list 反转和排序 功能描述: 
函数原型: 
reverse();   //反转链表sort();        //链表排序 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void  printList (const  list <int >& L)  	for  (list <int >::const_iterator it = L.begin (); it != L.end (); it++) { 		cout  << *it << " " ; 	} 	cout  << endl ; } bool  myCompare (int  val1 , int  val2) 	return  val1 > val2; } void  test01 () 	list <int > L; 	L.push_back(90 ); 	L.push_back(30 ); 	L.push_back(20 ); 	L.push_back(70 ); 	printList(L); 	 	L.reverse(); 	printList(L); 	 	L.sort();  	printList(L); 	L.sort(myCompare);  	printList(L); } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
list 排序案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include  <list>  #include  <string>  class  Person  {public :	Person(string  name, int  age , int  height ) { 		m_Name = name; 		m_Age = age; 		m_Height = height ; 	} public :	string  m_Name;   	int  m_Age;       	int  m_Height;    }; bool  ComparePerson (Person& p1, Person& p2)  	if  (p1.m_Age == p2.m_Age) { 		return  p1.m_Height  > p2.m_Height; 	} 	else  	{ 		return   p1.m_Age < p2.m_Age; 	} } void  test01 ()  	list <Person> L; 	Person p1 ("刘备" , 35  , 175 )  ; 	Person p2 ("曹操" , 45  , 180 )  ; 	Person p3 ("孙权" , 40  , 170 )  ; 	Person p4 ("赵云" , 25  , 190 )  ; 	Person p5 ("张飞" , 35  , 160 )  ; 	Person p6 ("关羽" , 35  , 200 )  ; 	L.push_back(p1); 	L.push_back(p2); 	L.push_back(p3); 	L.push_back(p4); 	L.push_back(p5); 	L.push_back(p6); 	for  (list <Person>::iterator it = L.begin (); it != L.end (); it++) { 		cout  << "姓名: "  << it->m_Name << " 年龄: "  << it->m_Age               << " 身高: "  << it->m_Height << endl ; 	} 	cout  << "---------------------------------"  << endl ; 	L.sort(ComparePerson);  	for  (list <Person>::iterator it = L.begin (); it != L.end (); it++) { 		cout  << "姓名: "  << it->m_Name << " 年龄: "  << it->m_Age               << " 身高: "  << it->m_Height << endl ; 	} } int  main ()  	test01(); 	system("pause" ); 	return  0 ; } 
关联式容器 使用关联式容器存储的元素,都是一个个的“键值对”( ) 。 
C++ STL提供了4种关联式容器,map, set, multimap, multiset
关联式容器名称 
特点 
 
 
map 
定义在 \ 头文件中,使用该容器存储的数据,其各个元素的键值必须是唯一的(不能重复),该容器会根据键值的大小进行自动升序排序  
 
set 
定义在 \ 头文件中,使用该容器存储的数据,各个元素键值完全相同,且各个元素的值不能重复(保证各元素键的唯一性),该容器会根据键值的大小进行自动升序排序。  
 
multimap 
与 map 区别在于,键值可以重复 
 
multiset 
存储的元素的值可以重复(一旦重复,意味着键也是重复的) 
 
 
 
pair 类模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include  <iostream>  #include  <utility>      // pair  #include  <string>       // string  using  namespace  std ;int  main ()           pair <string , double > pair1;          pair <string , string > pair2("STL教程" ,"http://stl/" );          pair <string , string > pair3(pair2);          pair <string , string > pair4(make_pair("C++教程" , "http://cplus/" ));          pair <string , string > pair5(string ("Python教程" ), string ("http://python/" ));     cout  << "pair1: "  << pair1.first << " "  << pair1.second << endl ;     cout  << "pair2: " << pair2.first << " "  << pair2.second << endl ;     cout  << "pair3: "  << pair3.first << " "  << pair3.second << endl ;     cout  << "pair4: "  << pair4.first << " "  << pair4.second << endl ;     cout  << "pair5: "  << pair5.first << " "  << pair5.second << endl ;     return  0 ; } 
1 2 3 4 5 pair1:  0 pair2: STL教程 http://stl/ pair3: STL教程 http://stl/ pair4: C++教程 http://cplus/ pair5: Python教程 http://python/ 
在 pair4 中,make_pair() 是 \ 头文件中提供的,主要功能是生成一个 pair 对象,使得 make_pair 函数所创造的返回值作为一个临时对象传递给 pair() 的第4种构造函数—> 移动构造函数(不是拷贝构造函数)。 
pair 的赋值操作 
1 2 pair1.first = "C++" ; pair1.second = "https://cplus.com" ; 
1 pair<string , string > pair = make_pair("c++" ,"https://cplus.com" ); 
\ 还提供了对于 pair 对象重载了 <, <=, >, >==, ==, != 运算符,可以进行比较大小之类的操作。 
1 2 3 4 5 6 7 pair<string, int> pair1("C++", 100); pair<string, int> pair2("C++", 100); if (pair1 == pair2){  std ::cout << pair1 == pair2 <<std ::endl ; }else {   std ::cout << pair1 != pair2 <<std ::endl ; } 
无序关联式容器 unordered_map 无序关联式容器与关联式容器最主要的区别在于,无序关联式容器不会对存储的元素做默认的升序排序操作。
无序关联式容器擅长通过指定键来查找对应的值,而遍历容器中存储元素的效率不如关联式容器。
关联式容器的底层实现采用的树存储结构——红黑树结构。
无序容器的底层采用的是哈希表的存储结构。
无序容器 
功能 
 
 
unordered_map 
存储键值对  类型的元素,其中各个键值对键的值不允许重复,且该容器中存储的键值对是无序的。  
 
unordered_multimap 
和 unordered_map 唯一的区别在于,该容器允许存储多个键相同的键值对。 
 
unordered_set 
不再以键值对的形式存储数据,而是直接存储数据元素本身(当然也可以理解为,该容器存储的全部都是键 key 和值 value 相等的键值对,正因为它们相等,因此只存储 value 即可)。另外,该容器存储的元素不能重复,且容器内部存储的元素也是无序的。 
 
unordered_multiset 
和 unordered_set 唯一的区别在于,该容器允许存储值相同的元素。 
 
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include  <iostream>  #include  <string>  #include  <unordered_map>  using  namespace  std ;int  main ()          std ::unordered_map <std ::string , std ::string > myMap; 	myMap["C++" ] = "cpp" ; 	myMap["Py" ] = "python" ; 	myMap["Java" ] = "java" ;          string  str = myMap.at("Java" );     cout  << "str = "  << str << endl ;          for  (auto  iter = myMap.begin (); iter != myMap.end (); ++iter)     {                  cout  << iter->first << " "  << iter->second << endl ;     }     return  0 ; } 
1 2 3 4 5 6 7 8 unorderedMaptest.cpp:19:10: warning: 'auto' type specifier is a C++11 extension [-Wc++11-extensions]     for (auto iter = myMap.begin(); iter != myMap.end(); ++iter)          ^ 1 warning generated. str = java Java java Py python C++ cpp 
向 unordered_map 中插入元素的方法 
emplace 方法效率比 insert 高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include  <iostream>  #include  <string>  #include  <unordered_map>  using  namespace  std ;int  main ()          unordered_map <string , string > umap;          pair<unordered_map <string , string >::iterator, bool > ret;          ret = umap.emplace("STL教程" , "http://c.biancheng.net/stl/" );          cout  << "bool ="  << ret.second << endl ;     cout  << "iter ->"  << ret.first->first << " "  << ret.first->second << endl ;     return  0 ; } 
tuple tuple<>为pair模板的泛化。
Morden C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 return  vector <int >(begin (ans),end (ans));return  {begin (ans), end (ans)};std ::unique_ptr <Foo> p = std ::make_unique<Foo>(...);auto  p = std ::make_unique<Foo>(...);for (auto  it = s.begin (); it != s.end (); ++it) ...auto  it = s.find (x);map <string , int > m;for (auto && kv : m){    cout << kv.first<<" "  << kv.secnod << endl ; } for (auto &&[key, val] : m){    cout <<key<<" "  << val <<endl ; } int  a = 3 , b =4 ;auto [x, y] = std ::tie(a, b);	int  &x = a;int  &y = b;
lambda 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 bool  comp (const  pair<int , int >&a, const  pair<int ,int >&b)     return  a.second < b.second; } int  main ()     vector <pair<int ,int >> v = ...;     std ::sort(begin (v), end (v), comp); } std ::sort(begin (v), end (v), [](const  auto & a, const  auto & b){    return  a.second < b.second; }); int  a = 1 ;auto  foo = [a]( a += 1 );foo(); ASSERT(a==1 ); int  a = 1 ;auto  foo = [&a]( a += 1 );foo(); ASSERT(a==2 ); int  a, b, c;auto  foo = [=]{return  a + b + c; }  auto  foo = [=, &a]{ a += b + c; }
new 内存分配 1 2 3 4 5 6 7 8 9 10 11 void  *__CRTDECL operator  new (size_t  size )  _THROW1 (_STD bad_alloc)          {               void  *p;         while  ((p = malloc (size )) == 0 )                 if  (_callnewh(size ) == 0 )                 {                                _THROW_NCEE(_XSTD bad_alloc, );                 }         return  (p);         } 
new 调用C语言标准库的 malloc 进行内存分配,并且在内存分配失败的时候会调用 _callnewh 来调用我们自定义的 new-handler,如果 new-handler 返回非零,new 运算符就会再次尝试内存分配,直到分配内存或 new-handler 返回了零,那么 new 此时将会以抛异常的形式来表示内存分配失败。
0拷贝 概念:一种避免CPU将数据从一块存储拷贝到另一块存储的技术,是指将数据直接从磁盘文件复制到网卡设备中,不需要经由程序操作。
好处:避免操作系统内核缓冲区之间进行数据拷贝操作;避免操作系统内核和用户应用程序地址空间两者之间进行数据拷贝操作。
实现及原理:
mmap() + write(). 
 
系统调用函数把内核缓冲区数据映射到用户空间,DMA将磁盘数据拷贝到内核缓冲区,应用进程跟随操作系统内核共享这个缓冲区。应用进程再次调用 write() ,操作系统直接将内核缓冲区的数据拷贝到 socket 缓冲区中,一切过程都发生在内核态。
sendfile() 
 
Linux 内核2.1中,提供专门发送文件的系统调用 sendfile() 。
由sendfile() 发起系统调用,操作系统由用户态空间切换到内核态空间 
通过DMA引擎将数据从磁盘拷贝到内核态空间的输入socket缓冲区 
将数据从内核空间拷贝到与之关联的socket缓冲区 
将socket缓冲区的数据拷贝到协议引擎中 
sendfile()系统调用结束,操作系统由用户态切换到内核态空间。 
 
inline define 直接调用的区别 
define 宏定义是单纯的字符替换,inline为代码嵌入(无函数调用跳转,无栈帧小号) 
处理阶段,define在预处理阶完成字符替换,inline 在编译阶段完成 
类型安全检查,define 无安全类型检查,inline 是一个函数,会进行类型安全检查 
 
进程 线程 协程 干货 | 进程、线程、协程 10 张图讲明白了! - 知乎 (zhihu.com) 
地址空间分布 
操作系统采用虚拟内存技术,把进程虚拟地址空间划分为用户空间和内核空间。
用户空间
Linux 可用 size 命令查看编译后程序的各个内存区域大小
1 2 3 (base) root@cy:~/rust_Pro# size hello_world/target/release/hello_world     text    data     bss     dec     hex filename  341457    13448      536   355441    56 c71 hello_world/target/release/hello_world 
内核空间
x86 32位系统,Linux 内核地址空间指虚拟地址从 0xC0000000开始到 0xFFFFFFFF位置的高端内存地址空间,总容量1G的容量。包含,内核镜像、物理页面表、驱动程序等运行在内核空间。
进程
进程是程序执行的过程 ,包括了动态创建、调度和消亡的整个过程,进程是程序资源管理的最小单位 。
线程
线程是操作系统能够进行运算调度的最小单位。线程被包含在进程之中,是进程中实际运作单位,一个进程可以包含多个线程,线程是资源调度的最小单位 。
线程资源和开销
同一进程中多条线程共享全部该进程中的系统资源,如虚拟地址空间,文件描述符文件描述符和信号处理等。但同一进程中多个线程有各自的调用栈、寄存器环境、线程本地存储等。
线程创建的开销主要是线程堆栈的建立,分配内存的开销,开销不大,其中最大的开销发生在线程上下文切换的时候。
协程:轻量化的微线程。 
协程的调度完全由用户控制,协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,再切回来时,回复先前保存的寄存器上下文和栈,直接将操作用户空间栈,完全没有内核切换的开销。
动态协程:协程的栈大小2KB,最大到1GB,可动态伸缩。而线程存储上下文寄存器和栈固定为2MB,不灵活且相对较大。