目录
内存分区
cpp将内存划分为4个区域
- 代码区(程序执行前):存放函数二进制代码
- 全局区(程序执行前):存放全局变量、静态变量以及常量
- 栈区:由编译器自动分配释放,存放函数参数值,局部变量
- 堆区:由人分配和释放(new,delete),程序结束之后系统会回收
#include<iostream>
using namespace std;
// 全局变量
int g_a = 0;
int g_b = 1;
// 全局常量
const int c_g_a = 0;
int main()
{
// 内存分为四个区
// 代码区,全局区,栈区,堆区
cout<<"全局变量g_a的地址:"<<&g_a<<endl;
cout<<"全局变量g_b的地址:"<<&g_b<<endl;
// 局部变量
int a = 0;
int b = 1;
cout<<"局部变量a的地址:"<<&a<<endl;
cout<<"局部变量b的地址:"<<&b<<endl;
// 静态变量
static int s_a = 0;
static int s_b = 1;
cout<<"静态变量s_a的地址:"<<&s_a<<endl;
cout<<"静态变量s_b的地址:"<<&s_b<<endl;
//常量
cout<<"字符串常量 hello world 的地址:"<<&("hello world")<<endl;
cout<<"全局常量c_g_a的地址:"<<&(c_g_a)<<endl;
// 局部常量
const int c_l_a = 0;
cout<<"局部常量c_l_a的地址:"<<&(c_l_a)<<endl;
return 0;
}
全局变量g_a的地址:0x407030
全局变量g_b的地址:0x403010
局部变量a的地址:0x61fe1c
局部变量b的地址:0x61fe18
静态变量s_a的地址:0x407038
静态变量s_b的地址:0x403014
字符串常量 hello world 的地址:0x4040d7
全局常量c_g_a的地址:0x404004
局部常量c_l_a的地址:0x61fe14
观察上述的地址
栈区
不要返回局部变量的地址
#include<iostream>
using namespace std;
int* fun()
{
int a = 10;
return &a;
}
int main()
{
int* p = fun();
cout<<*p<<endl;//第一次可以打印正确的数字 10 ,因为编译器做了保留
cout<<*p<<endl;//第二次这个数据不再保留
return 0;
}
// warning: address of local variable 'a' returned (编译报错)
堆区
new / delete
int* fun()
{
int* a = new int(10);//在堆区开辟一个内存,值是10,然后将其的地址赋值给指针p
return a;
}
int main()
{
int* p = fun();
cout<<*p<<endl;//10
cout<<*p<<endl;//10
cout<<*p<<endl;//10
// 手动释放
delete p;
//创建一个堆区的数组
int* arr = new int[10];
for(int i=0;i<10;i++)
{
arr[i] = i;
}
for (int i = 0; i < 10; i++)
{
cout<<arr[i]<<endl;
}
// 手动释放
delete[] arr;
return 0;
}
引用&
起别名
int a = 10;
int& b = a;
cout<<"b = "<<b<<endl;//10
b = 20;
cout<<"a = "<<a<<endl;//20
- 引用必须初始化
- 引用初始化之后不能改变
引用做函数参数
#include<iostream>
using namespace std;
// 值传递
void swap01(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
// 地址传递
void swap02(int* a,int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//引用传递,参数理解为起的别名
void swap03(int& a,int& b)
{
int temp = a;
a = b;
b = a;
}
int main()
{
int a = 10;
int b = 99;
swap01(a,b);//并没有交换两个值
swap01(&a,&b);//成功交换
swap03(a,b);//成功交换
return 0;
}
引用做函数返回值
- 函数不能返回局部变量的引用
//不能返回局部变量的引用
int& fun01()
{
int a = 0;
return a;
}
int main()
{
int& ref = fun01();
cout<<ref<<endl;//运行错误
return 0;
}
- 函数的调用可以作为左值
// 函数的调用可以作为左值
int& fun02()
{
static int a = 0;//静态变量,存放在全局区,在程序结束后释放
return a;
}
int main()
{
int& ref2 = fun02();
cout<<"ref2 = "<<ref2<<endl;//0
cout<<"ref2 = "<<ref2<<endl;//0
// 函数的调用可以作为左值
fun02() = 1;
cout<<"ref2 = "<<ref2<<endl;//1
cout<<"ref2 = "<<ref2<<endl;//1
return 0;
}
引用的本质
引用的本质就是一个指针常量
int main()
{
int a = 10;
//自动转换为 int* const ref = &a; 指针常量是指向一个不可更改的常量,也说明了引用为什么不可更改
int& ref = a;
ref = 20;//内部发现是一个引用,自动帮我们转换为 *ref = 20;
cout<<"a = "<<a<<endl;
cout<<"ref = "<<ref<<endl;
return 0;
}
常量引用
常量引用主要用来修饰形参,防止误操作(防止实参被修改)
//常量引用,用来修饰形参,防止实参被修改
// 这个函数会将外部的实参修改掉
void printValue01(int& a)
{
a = 0;
cout<<"value + 10 = "<<a + 10<<endl;
}
// 这个函数保证了在函数内部外部的实参不被修改,如果想修改,编译不通过
void printValue02(const int& a)
{
// a = 0; 编译不通过
cout<<"value + 10 = "<<a + 10<<endl;
}
int main()
{
int b = 99;
printValue01(a);//会将a修改为0
printValue02(a);//a不会被修改
return 0;
}
函数重载需要注意的
引用作为函数参数
#include<iostream>
using namespace std;
// 函数重载需要注意的事项
void fun(int& a)// int& a = 10 不合法
{
cout<<"fun(int& a)"<<endl;
}
void fun(const int& b)// const int& a = 10 合法
{
cout<<"fun(const int& b)"<<endl;
}
int main()
{
int a = 10;
fun(a);//fun(int& a)
fun(10);//fun(const int& b)
return 0;
}
函数重载遇到默认参数
// 函数重载遇到默认参数
void fun01(int a,int b = 10)
{
cout<<"fun01(int a,int b = 10)"<<endl;
}
void fun01(int a)
{
cout<<"fun01(int a)"<<endl;
}
int main()
{
fun01(0)//编译不通过
}
类和对象
#include<string>
class Student
{
public:
string name;
int stuId;
void printInfo()
{
cout<<"name = "<<name<<", stuId = "<<stuId<<endl;
}
protected:
string address;
private:
string password;
public:
void set_address_password(string address,string password)
{
address = address;
password = password;
}
void get_address_password()
{
cout<<"address "<<address<<", password"<<password<<endl;
}
};
- public:类内可以访问,类外也可以访问
- protected:类内可以访问,类外不可以访问,可以访问继承的父类
- private:类内可以访问,类外不可以访问,不可以访问继承的父类
struct和class
cpp中,struct和class的唯一区别就是访问权限不同
- class 默认权限是 私有private
- struct 默认权限是 公共public
构造函数和析构函数
构造函数和析构函数都是必须有的实现,如果不提供,编译器会提供至少3个函数:默认构造函数(空实现)、析构函数(空实现)、拷贝构造函数 (值拷贝)
如果用户定义了有参构造函数,cpp不会提供无参构造函数,但是会提供拷贝构造函数
如果用户定义了拷贝构造函数,cpp不会提供其它构造函数
-
构造函数
可以重载
创建对象时调用
-
析构函数
不可以重载
程序的生命周期终结时调用
#include<iostream>
using namespace std;
class Person
{
public:
Person()
{
cout<<"Person 构造函数"<<endl;
}
~Person()
{
cout<<"Person 析构函数"<<endl;
}
};
void test01()
{
Person p;
}
int main()
{
test01();//该行执行完,构造和析构都会执行(理解对象的生命周期)
Person p;//该行执行完,构造会执行
cin.get();
return 0;
}
Person 构造函数
Person 析构函数
Person 构造函数
1
Person 析构函数
构造函数
3种构造的方法
class Person
{
private:
int age;
public:
Person()
{
cout<<"Person 无参函数"<<endl;
}
Person(int age)
{
this->age = age;
cout<<"Person 有参构造"<<endl;
}
Person(const Person& p)
{
// 将传入的p的所有属性 拷贝到自身
this->age = p.age;
cout<<"Person 拷贝构造函数"<<endl;
}
~Person()
{
cout<<"Person 析构函数"<<endl;
}
};
void test01()
{
// 1
Person p;//默认构造
Person p1(10);//有参构造
Person p2(p1);//拷贝构造
// 默认构造函数 不要加()
//Person p4();//这种写法并不会创建一个对象,编译器会认为这是一个函数声明
//2 显示法
Person p5;
Person p6 = Person(20);
Person p7 = Person(p5);
// 匿名对象
// Person(30);//当前执行结束后,系统会立即回收
//注意: 不要用拷贝构造函数 来创建匿名对象
//Person(p3);
// 3 隐式转换
Person p8 = 10;
Person p9 = p8;
}
Person fun()
{
Person p1;
return p1;//这里返回的并不是p1对象,而是执行力拷贝构造函数,创建了一个新的对象
}
深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
如果有属性是在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
class Car
{
private:
int car_id;
double* car_value;
public:
Car()
{
cout<<"无参构造函数";
}
Car(int id,double value)
{
car_id = id;
car_value = new double(value);//在堆区new一个空间,返回一个指针
cout<<"有参构造函数";
}
//自己实现拷贝构造函数,解决编译器提供的拷贝构造函数带来的浅拷贝问题
Car(const Car& car)
{
car_id = car.car_id;
//car_value = car.car_value;//编译器提供的拷贝构造函数,就是这行代码,浅拷贝
// 深拷贝,解决car_value内存释放冲突问题
car_value = new double(*car.car_value);
}
~Car()
{
if (car_value != NULL)//car_value是在堆区手动创建的,需要手动释放
{
delete car_value;
car_value = NULL;
}
cout<<"析构函数"<<endl;
}
};
初始化列表
class A
{
private:
int A_a;
int A_b;
int A_c;
public:
A(int a,int b,int c):A_a(a),A_b(b),A_c(c){}
};
int main()
{
A aa(1,3,4);
return 0;
}
A类对象作为B类的属性
class Phone
{
string name;
}
class Person
{
int id;
Phone phone;
}
- 构造时先构建A的对象,再构造B对象自身
- 析构函数相反
静态成员
- 所有对象都共享同一份数据或函数
- 编译阶段就分配内存
- 类内声明,类外初始化
- 静态成员函数只能访问静态的成员变量
class A
{
private:
static string name;//类外访问不到
public:
static int id;
static void fun()
{
cout<<id<<endl;//只能访问静态成员变量
cout<<"静态成员函数fun";
}
};
string A::name = "zhangsan";
int A::id = 0;
int main()
{
Person p1 = Person();
//访问方式1
p1.name;
//访问方式2
Person::name;
return 0;
}
成员变量和成员函数分开存储
空对象占用的内存空间为 1
编译器会给每个空对象分配 1 个字节空间,为了区分空对象占内存的地址,每个空对象也应该有一个独一无二的内存地址
class A
{
};
int main()
{
A a;//空对象 sizeof(a) = 1
}
class B
{
int aa;//属于类的对象
static int bb;//不属于类的对象
void fun();//不属于类的对象
};
int main()
{
B b;//sizeof(b) = 4
}
this指针
- 解决命名冲突
- 返回对象本身 *this
class Person
{
int age;
Person(int age)
{
this->age = age;//this指针解决命名冲突
}
Person& addAge01(Person& p)//返回 引用
{
this->age += p.age;
return *this;//返回对象本体
}
Person addAge02(Person& p)//返回 值
{
this->age += p.age;
return *this;//返回一个新的对象(调用拷贝构造函数)
}
};
空指针访问成员
class Phone
{
int age;
void showClass()
{
cout<"this is class Phone";
}
void showAge()
{
if (this == NULL)
{
return;
}
cout<<"age is "<<age<<;//此处的age其实是this->age
}
}
int main()
{
Phone* p = NULL;
phone.showClass();//正常执行
phone.showAge();//报错 空指针异常
return 0;
}
const修饰成员函数
常函数
- 常函数不可以修改成员属性
- 成员属性声明时加
mutable
,在常函数中就可以修改
class A
{
//this指针本质是一个指针常量,指针的指向是不可修改的
//后面加const,意味着指针指向的值也不可以修改
void showA() const
{
//this->age = 10;//会报错,this指针不可以修改
this->id = 1;//正常执行
}
int age;
mutable id;//特殊变量,在常函数和常对象中可以修改
}
常对象
-
声明对象前加
const
-
常对象只能调用常函数
void fun()
{
const Person p;//在对象前加const,变为常对象
//p.age = 10;//错误,不能被修改
p.id = 1;//正确,可以修改
p.showA();//正确,可以执行
}
友元
全局函数作友元,可以访问类的私有成员
#include<iostream>
#include<string>
using namespace std;
class Home
{
//声明全局函数friend_fun是一个友元,就可以访问Home中的的私有成员
friend void friend_fun(Home* home);
public:
Home()
{
this->sitting_room = "客厅";
this->bad_room = "卧室";
}
string sitting_room;
private:
string bad_room;
};
void friend_fun(Home* home)
{
cout<<"公共属性 "<<home->sitting_room<<endl;
cout<<"私有属性 "<<home->bad_room<<endl;
}
int main()
{
Home h1;
friend_fun(&h1);
return 0;
}
类作友元
class TV
{
public:
Home* home;
TV()
{
this->home = new Home;
}
}
class Home
{
//类作友元,
friend class TV;
public:
Home()
{
this->sitting_room = "客厅";
this->bad_room = "卧室";
}
string sitting_room;
private:
string bad_room;
};
成员函数作友元
class TV
{
private:
Home* home;
public:
void visit01()
{
cout<<home->sitting_room<<endl;
cout<<home->bad_room<<endl;
}
void visit02()
{
cout<<home->sitting_room<<endl;
//cout<<home->bad_room<<endl;//不可访问
}
}
class Home
{
//声明另一个类的成员函数作为友元
friend void TV::visit01();
public:
Home()
{
this->sitting_room = "客厅";
this->bad_room = "卧室";
}
string sitting_room;
private:
string bad_room;
};
运算符重载
+
运算 符重载,实现两个自定义对象的相加
- 成员函数重载
- 全局函数重载
#include<iostream>
using namespace std;
class Person
{
public:
Person(){}
Person(int a,int b)
{
this->m_a = a;
this->m_b = b;
}
// 1 成员函数重载 + 运算符
Person operator+(Person& p)
{
Person temp;
temp.m_a = this->m_a + p.m_a;
temp.m_b = this->m_b + p.m_b;
return temp;
}
int m_a;
int m_b;
};
// 2 全局函数重载 + 运算符
Person operator+(Person& p1,Person& p2)
{
Person p3;
p3.m_a = p1.m_a + p2.m_a;
p3.m_b = p1.m_b + p2.m_b;
return p3;
}
//函数重载的版本
Person operator+(Person& p,int num)
{
Person p3;
p3.m_a = p.m_a + num;
p3.m_b = p.m_b + num;
return p3;
}
int main()
{
//实现两个自定义数据类型相加的运算
Person p1 = Person(2,3);
Person p2 = Person(10,20);
Person p3 = p1 + p2;
Person p4 = p1 + 99;
cout<<"p3.m_a = "<<p3.m_a<<endl;//12
cout<<"p3.m_b = "<<p3.m_b<<endl;//23
cout<<"p4.m_a = "<<p4.m_a<<endl;//101
cout<<"p4.m_b = "<<p4.m_b<<endl;//102
return 0;
}
<<
运算符重载
实现输出一个自定义对象的各属性
class Person
{
friend ostream& operator<<(ostream& out,Person p);
public:
Person(){}
Person(int a,int b)
{
this->m_a = a;
this->m_b = b;
}
private:
int m_a;
int m_b;
};
//只能用全局函数实现 重载<<运算符
ostream& operator<<(ostream& out,Person p)
{
out<<"m_a = "<<p.m_a<<" m_b = "<<p.m_b;//需要将这个函数声明为友元,访问私有属性
return out;
}
int main()
{
Person p(3,5);
cout<<p<<endl;
return 0;
}
++
运算符重载
#include<iostream>
using namespace std;
class MyInteger
{
friend ostream& operator<<(ostream& out,MyInteger myint);
public:
MyInteger(){}
MyInteger(int m)
{
this->m_num = m;
}
//重载 前置++ 运算符
MyInteger& operator++()//用MyInteger& 是为了对自身进行返回,一直对一个数据进行++
//如果 用MyInteger,则会新创建一个MyInteger对象
{
//先进行++运算
this->m_num++;
// 在将自身返回
return *this;
}
//重载 后置++ 运算符
MyInteger operator++(int)//int表示站位参数,可以用于区分前置和后置
{
// 先记录最开始的值
MyInteger temp = *this;
// 再递增
this->m_num++;
// 返回最开始的值
return temp;//局部对象,只能返回值,不能返回引用
}
private:
int m_num;
};
// 重载<<运算符
ostream& operator<<(ostream& out,MyInteger myint)
{
out<<myint.m_num;
return out;
}
int main()
{
MyInteger myint(0);
cout<<++(++myint)<<endl;//2
MyInteger myint02(10);
cout<<(myint02++)<<endl;//10
cout<<myint02<<endl;//11
return 0;
}
=
运算符重载
#include<iostream>
using namespace std;
class Person
{
public:
int* m_age;
Person(){}
Person(int age)
{
this->m_age = new int(age);
}
~Person()
{
if (this->m_age != NULL)
{
delete this->m_age;
this->m_age = NULL;
}
}
Person& operator=(Person& p)
{
//编译器提供浅拷贝,会出现内存释放冲突
//this->m_age = p.m_age;
//1 应该先判断是否有属性在堆区,如果有,先释放干净,然后再深拷贝
if (this->m_age != NULL)
{
delete this->m_age;
this->m_age = NULL;
}
//深拷贝
this->m_age = new int(*p.m_age);
return *this;
}
};
int main()
{
Person p1(20);
Person p2;
p2 = p1;
cout<<"p2 = "<<*p2.m_age<<endl;
return 0;
}
类对象=
的重载
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(int age,string name)
{
this->m_age = age;
this->m_name = name;
}
//重载两个对象的关系运算符
bool operator==(Person& p)
{
if (this->m_age == p.m_age && this->m_name==p.m_name)
{
return true;
}
else
{
return false;
}
}
bool operator!=(Person& p)
{
if (this->m_age == p.m_age && this->m_name==p.m_name)
{
return false;
}
else
{
return true;
}
}
int m_age;
string m_name;
};
int main()
{
Person p1(20,"zhangsan");
Person p2(20,"zhangsan");
if (p1 == p2)
{
cout<<"p1和p2 相等"<<endl;
}
else
{
cout<<"p1和p2 不相等"<<endl;
}
if (p1 != p2)
{
cout<<"p1和p2 不相等"<<endl;
}
else
{
cout<<"p1和p2 相等"<<endl;
}
cin.get();
return 0;
}
重载函数调用()
运算符——仿函数
#include<iostream>
#include<string>
using namespace std;
class printClass
{
public:
// 重载函数调用()
void operator()(string s)
{
cout<<s<<endl;
}
};
class addClass
{
public:
int operator()(int a,int b)
{
return a+b;
}
};
int main()
{
printClass p;
p("hello cpp!");
addClass a;
int res = a(3,7);
cout<<res<<endl;
//匿名函数
cout<<addClass()(1,9)<<endl;
cin.get();
return 0;
}
继承
降低代码重复
父类:基类
子类:派生类
#include <iostream>
using namespace std;
class Page
{
public:
void header()
{
cout << "页面头部内容。。" << endl;
}
void footer()
{
cout << "页面尾部内容。。" << endl;
}
};
// 继承
class BuyPage : public Page
{
public:
void content()
{
cout << "购买页面" << endl;
}
};
class Browse : public Page
{
public:
void content()
{
cout << "浏览页面" << endl;
}
};
int main()
{
BuyPage buy;
buy.header();
buy.footer();
buy.content();
Browse browse;
browse.header();
browse.footer();
browse.content();
return 0;
}
继承方式
#include <iostream>
using namespace std;
class Father
{
public:
int m_a = 1;
protected:
int m_b = 2;
private:
int m_c = 3;
};
class Son01 : public Father
{
public:
void test01()
{
cout << m_a << endl; // 公共
cout << m_b << endl; // 保护
// cout<<m_c<<endl;//不能访问
}
};
class Son02 : protected Father
{
public:
void test02()
{
cout << m_a << endl; // 变为保护
cout << m_b << endl; // 变为保护
// cout<<m_c<<endl;//不能访问
}
};
class Son03 : private Father
{
public:
void test03()
{
cout << m_a << endl; // 变为私有
cout << m_b << endl; // 变为私有
//cout<<m_c<<endl;//不能访问
}
};
继承中对象属性大小
父类中的所有非静态成员属性都会被子类继承下去,只是某些属性的权限不同
class Father
{
public:
int m_a = 1;
protected:
int m_b = 2;
private:
int m_c = 3;
};
class Son04 : public Father
{
public:
int m_d = 0;
};
void test()
{
//私有成员也被继承了,只是访问不到而已
cout << "size of : "<<sizeof(Son04) << endl;//16
}
继承中的构造和析构顺序
- 先构造父类,再构造子类
- 析构顺序相反
class Base
{
Base()
{
cout<<"Base 构造函数。。"<<endl;
}
~Base()
{
cout<<"Base 析构函数。。"<<endl;
}
}
继承同名成员函数
- 访问子类同名成员函数,直接访问即可
- 访问父类同名成员函数,需要加作用域
- 如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数(包括重载的),需要加作用域
class Base
{
Base()
{
int m_a = 100;
}
public:
print_hh()
{
cout<<"Base: hh"<<endl;
}
print_hh(int a)
{
cout<<"Base:hh and "<<a<<endl;
}
}
class Son:public Base
{
Son()
{
int m_a = 200;
}
public:
print_hh()
{
cout<<"Son: hh"<<endl;
}
}
test()
{
Son s;
cout<<"Son: m_a"<<s.m_a<<endl;//200
cout<<"Base: m_a"<<s.Base::m_a<<endl;//100 父类下的,需要加作用域
s.print_hh();//Son: hh
s.Base::print_hh();//Base: hh
//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数(包括重载的)
s.Base::print_hh(1);
return 0;
}
继承同名静态成员
子类,直接访问
父类,加作用域
class Base
{
public:
static int s_a;
static void fun()
{
cout << "Base--fun()" << endl;
}
};
int Base::s_a = 10;
class Son : public Base
{
public:
static int s_a;
static void fun()
{
cout << "Son--fun()" << endl;
}
};
int Son::s_a = 20;
void test01()
{
Son s;
// 通过对象访问
cout << s.s_a << endl; // 20
cout << s.Base::s_a << endl; // 10
s.fun(); // Son--fun()
s.Base::fun(); // Base--fun()
// 通过类名访问
cout << Son::s_a << endl; // 20
cout << Son::Base::s_a << endl; // 10
Son::fun();
Son::Base::fun();
}
多继承
class Base1
{
public:
Base1()
{
m_a = 1;
}
int m_a;
};
class Base2
{
public:
Base2()
{
m_a = 2;
}
int m_a;
};
class Son : public Base1, public Base2
{
public:
Son()
{
m_b = 3;
m_c = 4;
}
int m_b;
int m_c;
};
void test()
{
Son s;
cout << "size of : " << sizeof(s) << endl; // 16
// 不建议用多继承,因为会出现二义性,必须加作用域用于区分
cout << "Base1 :" << s.Base1::m_a << endl; // 1
cout << "Base2 :" << s.Base2::m_a << endl; // 2
}
菱形继承及解决方法(虚继承)
两个派生类继承同一个基类,又有某个类同时继承两个派生类
A
/ \
B C
\ /
D
#include <iostream>
using namespace std;
class Animal
{
public:
Animal()
{
m_age = 0;
}
int m_age;
};
// 虚继承
class Sheep : virtual public Animal
{
public:
Sheep()
{
m_age = 10;
}
};
// 虚继承
class Tuo : virtual public Animal
{
public:
Tuo()
{
m_age = 20;
}
};
class SheepTuo : public Sheep, public Tuo
{
};
int main()
{
SheepTuo st;
// cout<<st.m_age<<endl;//没有使用虚继承,报错,不明确
// 作用域区分
cout << "Sheep : " << st.Sheep::m_age << endl; // 10
cout << "Tuo : " << st.Tuo::m_age << endl; // 20
// 如果我们想明确m_age的继承,只需要继承一个m_age,在内存中只有一份,并且减少内存使用,需要使用虚继承
cout << st.m_age << endl; // 20
// m_age在内存中只有一份,Sheep父类和Tuo父类共享内存,指针指向同一个地址
st.Sheep::m_age = 50;
cout << st.m_age << endl; // 50
st.Tuo::m_age = 70;
cout << st.m_age << endl; // 70
return 0;
}
多态
虚函数,地址晚绑定,在运行阶段绑定函数地址,如果要实现多态,就需要使用虚函数
virtual
,如果不加virtual
,地址早绑定,在编译阶段确定函数地址
动态多态满足条件
- 有继承关系
- 子类重写父类的虚函数
#include <iostream>
using namespace std;
class Animal
{
public:
//虚函数,地址晚绑定,在运行阶段绑定函数地址,如果要实现多态,就需要使用虚函数virtual
//如果不加virtual,地址早绑定,在编译阶段确定函数地址
virtual void speak()
{
cout << "animal speaking" << endl;
}
};
class Cat : public Animal
{
public:
void speak()
{
cout << "cat speaking" << endl;
}
};
class Dog : public Animal
{
public:
void speak()
{
cout << "dog speaking" << endl;
}
};
void dospeaking(Animal &animal)
{
animal.speak();
}
int main()
{
Cat cat;
// dospeaking(cat); // animal speaking(Animal类中的speak()不加virtual)
dospeaking(cat); // cat speaking
Dog dog;
dospeaking(dog); // dog speaking
return 0;
}
开闭原则
- 对扩展进行开放
- 对修改进行关闭
多态实现计算器
- 可读性好
- 维护性高
#include <iostream>
using namespace std;
class AbstractCalcuator
{
public:
virtual int getResult()
{
return 0;
}
int number01;
int number02;
};
class AddCalcuator : public AbstractCalcuator
{
public:
int getResult()
{
return number01 + number02;
}
};
class SubCalcuator : public AbstractCalcuator
{
public:
int getResult()
{
return number01 - number02;
}
};
class MulCalcuator : public AbstractCalcuator
{
public:
int getResult()
{
return number01 * number02;
}
};
int main()
{
// 实现多态的条件
// 父类指针或引用指向子类对象
AbstractCalcuator *cal = new AddCalcuator; // 在堆区创建对象
cal->number01 = 10;
cal->number02 = 20;
cout << "number01 + number02 = " << cal->getResult() << endl;
// cal指向堆区对象,用完要释放
delete cal;
cal = new SubCalcuator; // 在堆区创建对象
cal->number01 = 10;
cal->number02 = 20;
cout << "number01 - number02 = " << cal->getResult() << endl;
// cal指向堆区对象,用完要释放
delete cal;
cal = new MulCalcuator; // 在堆区创建对象
cal->number01 = 10;
cal->number02 = 20;
cout << "number01 * number02 = " << cal->getResult() << endl;
// cal指向堆区对象,用完要释放
delete cal;
return 0;
}
纯虚函数和抽象类
-
抽象类无法实例化对象
-
子类必须重写纯虚函数,负责也属于抽象类
#include <iostream>
using namespace std;
class Base
{
public:
virtual void fun() = 0;
};
class Son : public Base
{
public:
void fun()
{
cout << "fun()函数正在调用。。" << endl;
}
};
int main()
{
Base *base = new Son;
base->fun();
return 0;
}
虚析构和纯虚析构
问题:多态使用时,如果有子类的属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决:将父类中的析构函数改为虚析构或者纯虚析构
虚析构或纯虚析构是用来解决父类指针释放子类对象
如果子类没有堆区数据,可以不写虚析构或纯虚析构
拥有纯虚析构函数的类也属于抽象类
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal构造函数" << endl;
}
// 利用虚析构,可以解决 父类指针释放子类对象时不干净的问题
virtual ~Animal()
{
cout << "Animal析构函数" << endl;
}
virtual void eat() // 多态的实现条件
{
cout << "animal eating..." << endl;
}
};
class Cat : public Animal
{
public:
Cat(string name)
{
m_name = new string(name); // 堆区创建一个变量
cout << "Cat构造函数" << endl;
}
~Cat()
{
if (m_name != NULL)
{
delete m_name;
m_name = NULL;
cout << "Cat析构函数" << endl;
}
}
string *m_name; // 一个指针类型的属性
void eat()
{
cout << *m_name << " eating..." << endl;
}
};
int main()
{
Animal *animal = new Cat("Tom");
animal->eat();
// 父类指针在析构时,不会调用子类的析构函数,导致如果子类有堆区属性,没有成功释放,出现内存泄露
delete animal;
return 0;
}
纯虚析构
也是一个抽象类,无法实例化对象
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal构造函数" << endl;
}
// 纯虚析构,类外必须实现
virtual ~Animal() = 0;
virtual void eat() // 多态的实现条件
{
cout << "animal eating..." << endl;
}
};
Animal::~Animal()
{
cout<<"Animal析构函数"<<endl;
}
class Cat : public Animal
{
public:
Cat(string name)
{
m_name = new string(name); // 堆区创建一个变量
cout << "Cat构造函数" << endl;
}
~Cat()
{
if (m_name != NULL)
{
delete m_name;
m_name = NULL;
cout << "Cat析构函数" << endl;
}
}
string *m_name; // 一个指针类型的属性
void eat()
{
cout << *m_name << " eating..." << endl;
}
};
int main()
{
Animal *animal = new Cat("Tom");
animal->eat();
// 父类指针在析构时,不会调用子类的析构函数,导致如果子类有堆区属性,没有成功释放,出现内存泄露
delete animal;
return 0;
}
组装案例
#include <iostream>
using namespace std;
class CPU
{
public:
virtual void calculate() = 0;
};
class Mermory
{
public:
virtual void storage() = 0;
};
class VideoCard
{
public:
virtual void display() = 0;
};
class Computer
{
public:
Computer(CPU *cpu, Mermory *mermory, VideoCard *videocard)
{
m_cpu = cpu;
m_mermory = mermory;
m_videocard = videocard;
}
void work()
{
m_cpu->calculate();
m_mermory->storage();
m_videocard->display();
}
~Computer()
{
if (m_cpu == NULL)
{
delete m_cpu;
m_cpu = NULL;
}
if (m_mermory == NULL)
{
delete m_mermory;
m_mermory = NULL;
}
if (m_videocard == NULL)
{
delete m_videocard;
m_videocard = NULL;
}
}
private:
CPU *m_cpu;
Mermory *m_mermory;
VideoCard *m_videocard;
};
class IntelCPU : public CPU
{
public:
void calculate()
{
cout << "Intel cpu working.." << endl;
}
};
class LenovoCPU : public CPU
{
public:
void calculate()
{
cout << "Lenovo cpu working.." << endl;
}
};
class IntelMermory : public Mermory
{
public:
void storage()
{
cout << "Intel mermory working.." << endl;
}
};
class LenoveMermory : public Mermory
{
public:
void storage()
{
cout << "Lenovo mermory working.." << endl;
}
};
class IntelVideoCrad : public VideoCard
{
public:
void display()
{
cout << "Intel videocard working.." << endl;
}
};
class LenovoVideoCrad : public VideoCard
{
public:
void display()
{
cout << "Lenovo videocard working.." << endl;
}
};
int main()
{
// 组装一台电脑
CPU *cpu01 = new IntelCPU;
Mermory *mermory01 = new IntelMermory;
VideoCard *videocard01 = new IntelVideoCrad;
Computer *computer01 = new Computer(cpu01, mermory01, videocard01);
computer01->work();
delete computer01;
Computer *computer02 = new Computer(new LenovoCPU, new LenoveMermory, new LenovoVideoCrad);
computer02->work();
delete computer02;
return 0;
}
文件操作
- 文本文件:以文本的ASCII码形式存储在计算机上
- 二进制文件:以二进制形式存储在计算机上
写文件
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
// 创建写文件流对象
ofstream ofs;
// 指定打开文件的方式
ofs.open("test01.txt", ios::out);
// 写内容
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:100" << endl;
ofs << "今天是2023年11月8日。。。" << endl;
// 关闭流对象
ofs.close();
return 0;
}
读文件
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
// 创建读文件流对象
ifstream ifs;
// 读取文件
ifs.open("test01.txt", ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败!!" << endl;
}
// 第一种
// char buffer[1024] = {0};
// while (ifs >> buffer)//按行读取
// {
// cout << buffer << endl;
// }
// 第二种
// char buffer[1024] = {0};
// while (ifs.getline(buffer, sizeof(buffer))) // 每次按行读取存储在buffer中,每次最大读取sizeof()
// {
// cout << buffer << endl;
// }
// 第三种
// string buffer;
// while (getline(ifs, buffer))
// {
// cout << buffer << endl;
// }
// 第四种
char c;
while ((c = ifs.get()) != EOF) // 逐个字符读取,EOF是文件末尾的标志
{
cout << c;
}
// 流关闭
ifs.close();
system("pause");
return 0;
}
二进制写文件
#include <iostream>
#include <fstream>
using namespace std;
class Person
{
public:
char name[100];
int age;
};
int main()
{
// 创建流对象
ofstream ofs("test02.txt", ios::out | ios::binary);
// 打开文件
// ofs.open("test02.txt",ios::out|ios::binary);
// 写文件
Person perosn = {"james", 40};
ofs.write((const char *)&perosn, sizeof(Person));
ofs.close();
return 0;
}
二进制读文件
#include <iostream>
#include <fstream>
using namespace std;
class Person
{
public:
char name[100];
int age;
};
int main()
{
// 创建读文件对象
ifstream ifs;
ifs.open("test02.txt", ios::in | ios::binary);
if (!ifs.is_open())
{
cout << "文件打开时失败!!" << endl;
}
// 读文件
Person person;
ifs.read((char *)&person, sizeof(Person));
cout << "name: " << person.name << ", age: " << person.age << endl;
ifs.close();
system("pause");
return 0;
}
模版
泛型编程
函数模版,类模版
函数模版
-
自动类型推导必须推导出一致的数据类型
-
模版必须要确定出T的数据类型
#include <iostream>
using namespace std;
// 模版函数
template <typename T>
void mySwap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
template <class T>
void printInfo()
{
cout << "it's great!" << endl;
}
int main()
{
int a = 10;
int b = 20;
// 1 自动类型推导
mySwap(a, b);
cout << "a = " << a << endl; // 20
cout << "b = " << b << endl; // 10
// 2 显式指定类型
mySwap<int>(a, b);
cout << "a = " << a << endl; // 10
cout << "b = " << b << endl; // 20
// 自动类型推导必须要推导出正确的类型
char c = 'q';
// mySwap(a,c);//错误的
// 模版必须要确定出T的类型
// printInfo();//错误的
printInfo<int>(); // 必须显式指定
system("pause");
return 0;
}
例子
#include <iostream>
using namespace std;
// 交换两个值的模版函数
template <typename T>
void my_swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
// 降序排序的模版函数
template <typename T>
void sortArr(T arr[], int len) // 降序
{
int i = 0;
while (i < len)
{
int max_idx = i;
for (int j = i + 1; j < len; j++)
{
if (arr[j] > arr[max_idx])
{
max_idx = j;
}
}
if (max_idx != i)
{
my_swap(arr[max_idx], arr[i]);
}
i++;
}
}
//打印不同类型数组的模版函数
template <typename T>
void printArr(T arr[], int len)
{
int i = 0;
while (i < len)
{
cout << arr[i];
i++;
}
cout << endl;
}
int main()
{
char charArr[] = "efcsab";
int lens01 = sizeof(charArr) / sizeof(char);
sortArr(charArr, lens01);
printArr(charArr, lens01);
int intArr[] = {2, 4, 6, 3, 0, 9, 3, 8};
int lens02 = sizeof(intArr) / sizeof(int);
sortArr(intArr, lens02);
printArr(intArr, lens02);
system("pause");
return 0;
}
普通函数和函数模板的区别
- 普通函数调用时可以发生自动类型转换
- 函数模版调用时,如果是自动类型推导,不会发生自动类型转换;如果是显式指定类型方式,可以发生自动类型转换
#include <iostream>
using namespace std;
int myadd01(int a, int b)
{
return a + b;
}
template <typename T>
T myadd02(T a, T b)
{
return a + b;
}
int main()
{
int a = 1;
int b = 99;
char c = 'A';
cout << myadd01(a, b) << endl;
cout << myadd01(1, c) << endl;
cout << myadd02(a, b) << endl;
// cout << myadd02(a, c) << endl; // 错误,无法推导出一致的数据类型
cout << myadd02<int>(a, c) << endl;//显式指定,可以发生自动类型转换
system("pause");
return 0;
}
- 函数模版和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模版可以发生重载
- 函数模版可以产生更好的匹配,优先调用函数模版
#include <iostream>
using namespace std;
void myprint(int a, int b)
{
cout << "01 普通函数" << endl;
}
template <typename T>
void myprint(T a, T b)
{
cout << "02 模版函数" << endl;
}
template <typename T>
void myprint(T a, T b, T c)
{
cout << "03 重载的模版函数" << endl;
}
int main()
{
int a = 1;
int b = 99;
char c = 'A';
myprint(a, b); // 01
myprint<>(a, b); // 02 通过空模板参数列表
myprint<int>(a, b, c); // 03 重载
myprint('A', 'a'); // 02
system("pause"); // 模版函数是更好的匹配
return 0;
}
既然使用了模版函数,最好不要再提供普通函数,因为会出现二义性
模版局限性和自定义
利用具体化的模版,可以解决自定义类型的通用化
#include <iostream>
using namespace std;
class Person
{
public:
string name;
int age;
};
template <typename T>
bool isEqual(T &a, T &b)
{
if (a == b)
{
return true;
}
else
{
return false;
}
}
// 利用具体化的Person版本实现,会优先调用
template <>
bool isEqual(Person &a, Person &b)
{
if (a.name == b.name && a.age == b.age)
{
return true;
}
else
{
return false;
}
}
int main()
{
Person p1 = {"zhangsan", 10};
Person p2 = {"zhangsan", 10};
cout << isEqual(p1, p2) << endl; // 1 ,会优先调用Person模板函数
system("pause");
return 0;
}
类模板
#include <iostream>
using namespace std;
template <class nameType, class ageType>
class Person
{
public:
Person(nameType name, ageType age)
{
m_name = name;
m_age = age;
}
nameType m_name;
ageType m_age;
};
int main()
{
Person<string, int> person = {"悟空", 5};
cout << person.m_name << endl;
cout << person.m_age << endl;
system("pause");
return 0;
}
类模板和函数模板的区别
- 类模板没有自动类型推导
- 类模板在参数列表中可以有默认参数
template <class nameType, class ageType=int>
class Person
{
public:
Person(nameType name, ageType age)
{
m_name = name;
m_age = age;
}
nameType m_name;
ageType m_age;
};
int main()
{
//Person person("八戒", 1000);//错误,不能自动类型推导
Person<string> person("悟空", 5000);
return 0;
}
类模板中成员函数创建时机
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
class A
{
public:
void showA()
{
cout<<"A.."
}
}
template<class T>
class B
{
public:
T obj;
//在调用时才创建,因为T不确定
void fun()
{
obj.showA();
}
}
类模板对象做函数参数
- 指定传入的参数类型(通常使用这种方法,直接了当)
- 参数模版化
- 整个类模版化
#include <iostream>
#include <string>
using namespace std;
template <class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age)
{
this->m_name = name;
this->m_age = age;
}
void show()
{
cout << "name:" << this->m_name << " age:" << this->m_age << endl;
}
T1 m_name;
T2 m_age;
};
// 1 指定传入的类型
void printPerson1(Person<string, int> &p)
{
p.show();
}
// 2 参数模版化
template <class T1, class T2>
void printPerson2(Person<T1, T2> &p)
{
p.show();
}
// 3 整个类模板化
template <class T>
void printPerson3(T &p)
{
p.show();
}
int main()
{
Person<string, int> p("悟空", 200);
printPerson1(p);
Person<string, int> p2("八戒", 100);
printPerson1(p2);
Person<string, int> p3("如来", 1000);
printPerson1(p3);
system("pause");
return 0;
}
类模板与继承
如果父类是一个类模版,子类需要指定出父类中T的数据类型
- 直接制定
- 继续使用类模板
#include <iostream>
#include <string>
using namespace std;
template <class T>
class Base
{
public:
T name;
};
// 1 直接指定父类中的T类型
class Sub : public Base<string>
{
};
// 2 类模板声明父类中的类型,才能继承
template <class T1, class T2>
class Son : public Base<T1>
{
public:
Son()
{
cout << "T1: " << typeid(T1).name() << endl;
cout << "T2: " << typeid(T2).name() << endl;
}
T2 age;
};
int main()
{
Sub s;
s.name = "zhang";
cout << "name:" << s.name << endl;
Son<string, int> s2;
system("pause");
return 0;
}
类模板中成员函数的类外实现
#include <iostream>
using namespace std;
template <class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
T1 m_name;
T2 m_age;
void showPerson();
};
// 构造函数类外实现
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 << "name: " << this->m_name << " age: " << this->m_age << endl;
}
int main()
{
Person<string, int> person("zhangsan", 18);
person.showPerson();
system("pause");
return 0;
}
类模板分文件编写
由于类模板中的成员函数创建时机是在调用阶段,在分文件编写时无法编译通过类模版的函数
解决方法:将类模板声明和实现写在一个文件里,约定俗称,以.hpp
结尾的文件
person.hpp
#pragma once
#include <iostream>
using namespace std;
template <class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
T1 m_name;
T2 m_age;
void showPerson();
};
// 构造函数类外实现
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 << "name: " << this->m_name << " age: " << this->m_age << endl;
}
test.cpp
#include <iostream>
using namespace std;
#include "person.hpp"
int main()
{
Person<string, int> person("zhangsan", 18);
person.showPerson();
system("pause");
return 0;
}
类模板和友元
全局函数作为友元,类外实现的话,需要让编译器提前知道全局函数的存在,需要将代码写在最前面
通常使用类内实现
#include <iostream>
using namespace std;
template <class T1, class T2>
class Person;
// 类外实现
template <class T1, class T2>
void printPerson02(Person<T1, T2> p)
{
cout << "name " << p.m_name << " age " << p.m_age << endl;
}
template <class T1, class T2>
class Person
{
// 1 全局函数,类内实现
friend void printPerson01(Person<T1, T2> p)
{
cout << "name " << p.m_name << " age: " << p.m_age << endl;
}
// 2 全局函数,类外实现
// 类外实现,需要让编译提前知道这个函数的存在
friend void printPerson02<>(Person<T1, T2> p);
public:
Person(T1 name, T2 age)
{
this->m_age = age;
this->m_name = name;
}
private:
T1 m_name;
T2 m_age;
};
int main()
{
Person<string, int> p1("zhang", 19);
printPerson01(p1);
Person<string, int> p2("li", 25);
printPerson02(p2);
system("pause");
return 0;
}
类模板实例-数组模版
.hpp
#pragma once
#include <iostream>
using namespace std;
template <class T>
class MyArray
{
public:
MyArray(int capacity)
{
this->my_capacity = capacity;
this->my_size = 0;
this->my_address = new T[this->my_capacity];
}
// 重写拷贝构造函数,避免浅拷贝带来的问题
MyArray(const MyArray &arr)
{
this->my_capacity = arr.my_capacity;
this->my_size = arr.my_size;
// 这是编译器提供的浅拷贝
// this->my_address = arr.my_address;
// 深拷贝
this->my_address = new T[arr.my_capacity];
for (int i = 0; i < this->my_size; i++)
{
this->my_address[i] = arr.my_address[i];
}
}
// operator= 防止浅拷贝问题
MyArray &operator=(const MyArray &arr)
{
if (this->my_address != NULL)
{
delete[] this->my_address;
this->my_address = NULL;
this->my_capacity = 0;
this->my_size = 0;
}
// 深拷贝
this->my_capacity = arr.my_capacity;
this->my_size = arr.my_size;
this->my_address = new T[arr.my_capacity];
for (int i = 0; i < this->my_size; i++)
{
this->my_address[i] = arr.my_address[i];
}
return *this;
}
~MyArray()
{
if (this->my_address != NULL)
{
// delete[] 释放数组中全部元素的内存空间
delete[] this->my_address;
this->my_address = NULL;
}
}
// 尾插法
void push_back(const T &val)
{
// 先判断目前的大小是否等于容量
if (this->my_size == this->my_capacity)
{
return;
}
// 尾部插入数据,并修改大小
this->my_address[this->my_size] = val;
this->my_size++;
}
// 尾删法
void pop()
{
if (this->my_size == 0)
{
return;
}
// 逻辑上的删除
this->my_size--;
}
// 通过下标方式访问 []
T &operator[](int index)
{
return this->my_address[index];
}
// 返回数组大小
int getSize()
{
return this->my_size;
}
// 返回数组容量
int getCapacity()
{
return this->my_capacity;
}
private:
int my_capacity; // 容量
int my_size; // 大小
T *my_address; // 维护的数组地址
};
.cpp
#include <iostream>
#include <string>
using namespace std;
#include "MyArray.hpp"
class Person
{
public:
Person(){};
Person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
int m_age;
string m_name;
};
void printArr(MyArray<int> &arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << arr[i] << endl;
}
}
void printPersonArr(MyArray<Person> &arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << "name:" << arr[i].m_name << ",age:" << arr[i].m_age << endl;
}
}
int main()
{
// 有参构造
// MyArray<int> arr01(5);
// // 拷贝构造
// MyArray<int> arr02(arr01);
// // =拷贝,先清空,再拷贝
// MyArray<int> arr03(10);
// arr03 = arr01;
MyArray<int> arr01(5);
for (int i = 0; i < 5; i++)
{
// 利用尾插法插入数据
arr01.push_back(i);
}
printArr(arr01);
cout << "01容量:" << arr01.getCapacity() << endl;
cout << "01大小:" << arr01.getSize() << endl;
MyArray<int> arr02(arr01);
arr02.pop();
printArr(arr02);
cout << "02容量:" << arr02.getCapacity() << endl;
cout << "02大小:" << arr02.getSize() << endl;
cout << "-----------------" << endl;
MyArray<Person> arr_person(5);
Person p1("zhangsan", 12);
Person p2("lisi", 25);
Person p3("wangwu", 36);
Person p4("hhh", 47);
arr_person.push_back(p1);
arr_person.push_back(p2);
arr_person.push_back(p3);
arr_person.push_back(p4);
printPersonArr(arr_person);
arr_person.pop();
cout << "person大小:" << arr_person.getSize() << endl;
system("pause");
return 0;
}
STL标准模板库
Standard Template Library
六大组件
- 容器:各种数据结构,vector,list,set,map
- 算法:排序,查找
- 迭代器:容器和算法之间的胶合剂
- 仿函数
- 适配器:用来修饰容器或者仿函数或迭代器的
- 空间配置器:负责空间配置与管理
Vector
存放内置数据类型
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void my_print(int val)
{
cout << val << endl;
}
int main()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
// 第一种遍历方式
// vector<int>::iterator v_begin = v.begin(); // 起始迭代器,指向第一个位置
// vector<int>::iterator v_end = v.end(); // 结束迭代器,指向最后一个元素的下一个位置
// while (v_begin != v_end)
// {
// cout << *v_begin << endl;
// v_begin++;
// }
// 第二种遍历方式
// for (vector<int>::iterator v_begin = v.begin(); v_begin < v.end(); v_begin++)
// {
// cout << *v_begin << endl;
// }
// 第三种遍历方式,STL提供的标准遍历算法
for_each(v.begin(), v.end(), my_print);
system("pause");
return 0;
}
存放自定义数据类型
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
};
void test01()
{
// 存放Person对象
vector<Person> v1;
Person p1("zhangsan", 1);
Person p2("lisi", 2);
Person p3("wangwu", 3);
v1.push_back(p1);
v1.push_back(p2);
v1.push_back(p3);
for (vector<Person>::iterator it = v1.begin(); it < v1.end(); it++)
{
// 两种方式
cout << "name:" << it->m_name << ",age:" << it->m_age << endl; // 指针
// cout << "name:" << (*it).m_name << ",age:" << (*it).m_age << endl; // 对象
}
}
void test02()
{
// 存放Person对象的地址
vector<Person *> v2;
Person p1("zhangsan", 1);
Person p2("lisi", 2);
Person p3("wangwu", 3);
v2.push_back(&p1);
v2.push_back(&p2);
v2.push_back(&p3);
for (vector<Person *>::iterator it = v2.begin(); it < v2.end(); it++)
{
// 解引用之后是Person*,对象的地址
cout << "name:" << (*it)->m_name << ",age:" << (*it)->m_age << endl;
}
}
int main()
{
test01();
cout << "------------" << endl;
test02();
system("pause");
return 0;
}
容器嵌套容器
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<vector<int>> v;
vector<int> v1;
vector<int> v2;
vector<int> v3;
for (int i = 0; i < 3; i++)
{
v1.push_back(i + 1);
v2.push_back(i + 4);
v3.push_back(i + 7);
}
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
for (vector<vector<int>>::iterator it = v.begin(); it < v.end(); it++)
{
for (vector<int>::iterator sub_it = (*it).begin(); sub_it < (*it).end(); sub_it++)
{
cout << *sub_it << "\t";
}
}//1 2 3 4 5 6 7 8 9
system("pause");
return 0;
}
常用容器
string
char*
是一个指针string
是一个类,内部封装了char*
,管理这个字符串,是一个char*
型的容器string
管理char*
所分配到内存,不用担心越界问题
- string();
- string(const char* s);
- string(const string& str);
- string(int n,char c);
int main()
{
// 1
string s1;
// 2
const char *str = "hello cpp";
string s2(str);
// 3
string s3(s2);
// 4
string s4(9, 'q');
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 str5;
str5 = "hello python";
string str6;
str6 = str5;
string str7;
str7 = 'a';
string str1;
str1.assign("hello cpp");
string str2;
str2.assign("hello cpp", 5);
string str3;
str3.assign(str2);
string str4;
str4.assign(3, 'q');
string
字符串拼接
#include <iostream>
#include <string>
using namespace std;
int main()
{
// 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 str1 = "我";
str1 += "喜欢编程";
cout << str1 << endl;
str1 += '!';
cout << str1 << endl;
string str2 = "python,cpp";
str1 += str2;
cout << str1 << endl;
string str3 = "I";
str3.append(" like ");
str3.append("program,game", 8);
cout << str3 << endl;
str3.append(str2, 7, 3); // 从第7个开始,截取3个字符
cout << str3 << endl;
system("pause");
return 0;
}
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 = ) const;//查找字符C最后一次出现位置
string& replace(int pos, int n, const string& str);//替换从pos开始n个字符为字符串str
string& replace(int pos, int n,const char* s);//替换从pos开始的n个字符为字符串s
string
字符串比较
主要用于比较两个字符串是否相等,比较大小意义不大
//大于返回1,小于返回-1,等于返回0
int compare(const string &s) const;//与字符串比较
int compare(const char *s) const;
string str1 = "hello";
string str2 = "abc";
str1.compare(str2);//1
string
字符串存取
char& operator[](int n);//[]取字符
char& at(int n);//取字符
string str1 = 'hello cpp';
for(int i=0;i<str1.size();i++)
{
cout<<str1[i]<<",";
}
for(int i=0;i<str1.size();i++)
{
cout<<str1.at(i)<<",";
}
str1[0] = 'q';
str1.at(1) = 'z';
string
插入和删除
string& insert(int pos, const char* s);//插入字符串
string& insert(int pos, const string& str);//插入字符串
string& insert(int pos, int n, char c);//在指定位置插入n个字符
string& erase(int pos, int n = npos);//删除从Pos开始的n个字符
string
子串
string substr(int pos = 0,int n = npos) const;//返回由pos开始的n个字符组成的字符串
Vector
vector和数组相似,也称为
单端数组
vector与普通数组的不同之处,vector可以动态扩展
并不是在原空间之后续接空间,而是拷贝到更大的新空间,并删除原空间
vector<T> v;//采用模板实现类实现,默认构造函数
vector(v.begin(),v.end());//将 v[begin(),end()) 区间中的元素拷贝给本身
vector(n,elem);//构造函数将n个elem拷贝给本身
vector(const vector &vec);//拷贝构造函数
构造函数
#include <iostream>
#include <vector>
using namespace std;
void print_vector(vector<int> v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << (*it) << ",";
}
cout << endl;
}
int main()
{
// 1 默认无参构造
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
print_vector(v1);
// 2 区间
vector<int> v2(v1.begin(), v1.end());
print_vector(v2);
// 3 n个元素
vector<int> v3(10, 9);
print_vector(v3);
// 4 拷贝
vector<int> v4(v3);
print_vector(v4);
system("pause");
return 0;
}
赋值
=
assign()
int main()
{
// 1 默认无参构造
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
// 赋值
vector<int> v5;
v5 = v1;
vector<int> v6;
v6.assign(v5.begin(), v5.end());
vector<int> v7;
v7.assign(10, 1);
}
vector
容量和大小
容量capacity自动扩容
empty();//判断容器是否为空
capacity();//容器的容量
size();//返回容器中元素的个数
resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则未尾超出容器长度的元素被删除。
resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除
#include <iostream>
#include <vector>
using namespace std;
void print_vector(vector<int> v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << (*it) << ",";
}
cout << endl;
}
int main()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
print_vector(v); // 0,1,2,3,4,5,6,7,8,9,
cout << "是否为空:" << v.empty() << endl; // 0
cout << "容量:" << v.capacity() << endl; // 16
cout << "大小:" << v.size() << endl; // 10
// 重设大小,指定填充的值,默认0填充
v.resize(15, -1);
print_vector(v); // 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,
cout << "容量:" << v.capacity() << endl; // 16
cout << "大小:" << v.size() << endl; // 15
//更短的话,多的数直接删除
v.resize(6);
print_vector(v); // 0,1,2,3,4,5
cout << "容量:" << v.capacity() << endl; // 16
cout << "大小:" << v.size() << endl; // 6
v.resize(30);
print_vector(v); // 0,1,2,3,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
cout << "容量:" << v.capacity() << endl; // 30
cout << "大小:" << v.size() << endl; // 30
system("pause");
return 0;
}
插入和删除
push back(ele);//尾部插入元素ele
pop_back();//删除最后一个元素
insert(const iterator pos, ele);//选代器指向位置pos插入元素ele
insert(const_iterator pos,int count,ele); //选代器指向位置pos插入count个元素ele//删除迭代器指向的元素erase(const iterator pos);
erase(const_iterator start,const iterator end); //删除选代器从start到end之间的元素
clear();//删除容器中所有元素
#include <iostream>
#include <vector>
using namespace std;
void print_vector(vector<int> v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << (*it) << ",";
}
cout << endl;
}
int main()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
print_vector(v); // 10,20,30,40,50,
// 尾部删除
v.pop_back();
print_vector(v); // 10,20,30,40,
// 插入,第一个参数是迭代器
v.insert(v.begin(), 0);
print_vector(v); // 0,10,20,30,40,
v.insert(v.begin(), 5, -1); //-1,-1,-1,-1,-1,0,10,20,30,40,
print_vector(v);
// 删除,参数是迭代器
v.erase(v.begin());
print_vector(v); //-1,-1,-1,-1,0,10,20,30,40,
// 清空1
v.erase(v.begin(), v.end());
print_vector(v);
// 清空2
v.clear();
print_vector(v);
system("pause");
return 0;
}
数据存取
at(int idx);//返回索引idx所指的数据
operator[];//返回索引idx所指的数据
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素
int main()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
cout<<v[1]<<endl;//20
cout<<v.at(2)<<endl;//30
cout<<v.front()<endl;//10
cout<<v.back()<<endl;//30
return 0;
}
vector
互换容器
swap
int main()
{
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
print_vector(v1); // 0,1,2,3,4,5,6,7,8,9,
vector<int> v2;
for (int i = -10; i < 0; i++)
{
v2.push_back(i);
}
print_vector(v2); //-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,
// 交换容器
v1.swap(v2);
print_vector(v1); //-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,
print_vector(v2); // 0,1,2,3,4,5,6,7,8,9,
return 0;
}
实际用途:收缩内存空间
// vector<int>(v)是一个匿名对象,交换空间之后,匿名对象指向原来的大容量容器,
// 匿名对象在执行完之后被系统自动回收,起到压缩空间的作用
vector<int>(v).swap(v);
void test()
{
vector<int> v;
cout << "容量:" << v.capacity() << endl; // 0
cout << "大小:" << v.size() << endl; // 0
for (int i = 0; i < 10000; i++)
{
v.push_back(i);
}
cout << "容量:" << v.capacity() << endl; // 16384
cout << "大小:" << v.size() << endl; // 10000
// 重设容器大小,容量并不会变,但是只用到非常少的空间
v.resize(10);
cout << "容量:" << v.capacity() << endl; // 16384
cout << "大小:" << v.size() << endl; // 10
// 利用swap压缩空间
// vector<int>(v)是一个匿名对象,交换空间之后,匿名对象指向原来的大容量容器,
// 匿名对象在执行完之后被系统自动回收,起到压缩空间的作用
vector<int>(v).swap(v);
cout << "容量:" << v.capacity() << endl; // 10
cout << "大小:" << v.size() << endl; // 10
}
vector
预留空间
reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问
统计容器在插入数据时新开辟内存的次数
void test()
{
vector<int> v;
cout << "容量:" << v.capacity() << endl; // 0
cout << "大小:" << v.size() << endl; // 0
int num = 0;
int *p = NULL; // 指针p指向容器的首元素地址,容器每次扩容都要新开辟内存,首元素地址就会变
for (int i = 0; i < 10000; i++)
{
v.push_back(i);
if (p != &v[0])
{
num++;
p = &v[0];
}
}
cout << "容量:" << v.capacity() << endl; // 16384
cout << "大小:" << v.size() << endl; // 10000
cout << "开辟次数:" << num << endl; // 15
}
当容器需要插入很大数据量时
容器开始没有预留空间,插入数据需要自动扩容,如果我们一直开始就知道要插入的元素个数,就可以利用reserve()
预留空间,减少空间开辟的次数。上面的代码需要扩容15次,下面的代码只需要扩容1次
void test()
{
vector<int> v;
cout << "容量:" << v.capacity() << endl; // 0
cout << "大小:" << v.size() << endl; // 0
v.reserve(10000);
cout << "容量:" << v.capacity() << endl; // 10000
cout << "大小:" << v.size() << endl; // 0
int num = 0;
int *p = NULL; // 指针p指向容器的首元素地址,容器每次扩容都要新开辟内存,首元素地址就会变
for (int i = 0; i < 10000; i++)
{
v.push_back(i);
if (p != &v[0])
{
num++;
p = &v[0];
}
}
cout << "容量:" << v.capacity() << endl; // 16384
cout << "大小:" << v.size() << endl; // 10000
cout << "开辟次数:" << num << endl; // 1
}
deque
双端数组,头部和尾部都可以操作
deque与vector区别:
- vector对于头部的插入删除效率低,数据量越大,效率越低。
- deque相对而言,对头部的插入删除速度回比vector快。
- vector访问元素时的速度会比deque快,这和两者内部实现有关
- deque也是支持随机访问的
deque<T> degT;//默认构造形式
deque( beg, end);//构造函数将[beg,end)区间中的元素拷贝给本身
deque(n, elem);//构造函数将n个elem拷贝给本身
deque(const deque &deq);//拷贝构造函数
构造函数
#include <iostream>
#include <deque>
using namespace std;
//限制为只读
void print_deque(const deque<int>& d)
{
for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++)
{
// *it = 10;//不能修改
cout << (*it) << " ";
}
cout << endl;
}
int main()
{
deque<int> d;
for (int i = 0; i < 10; i++)
{
d.push_front(i); // 前插
}
for (int i = 10; i < 20; i++)
{
d.push_back(i); // 后插
}
print_deque(d);
//区间
deque<int> d2(d.begin(), d.end());
print_deque(d2);
//10个1
deque<int> d3(10, 1);
print_deque(d3);
//拷贝构造
deque<int> d4(d3);
print_deque(d4);
system("pause");
return 0;
}
赋值
deque& operator=(const deque &deq);//重载等号操作符
assign(beg, end);//将[beg,end)区间中的数据拷贝赋值给本身
assign(n, elem);//将n个elem拷贝赋值给本身。
大小的操作
deque
没有容量的概念,它是不限容量的
deque.empty();//判断容器是否为空
deque.size();//返回容器中元素的个数
deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除
deque.resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除,
插入和删除
两端插入操作:
push_back(elem);//在容器尾部添加一个数据
push_front(elem);//在容器头部插入一个数据
pop_back();//删除容器最后一个数据
pop_front();//删除容器第一个元素
指定位置操作:
insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置,pos是迭代器对象
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值
insert(pos, beg,end);//在pos位置插入[beg,end)区间的数据,无返回值,pos,beg,end是迭代器对象
clear();//清空容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置,beg,end是迭代器对象
erase(pos);//删除pos位置的数据,返回下一个数据的位置,参数是迭代器
存取
at(int idx);//返回索idx所指的数据
operator[];//返回索引idx所指的数据
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素
排序
STL提供的排序函数
sort(iterator begin,iterator end);//对区间内的元素进行排序
对于支持随机访问的迭代器容器,都可以利用sort算法进行排序
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
// 限制为只读
void print_deque(const deque<int> &d)
{
for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++)
{
// *it = 10;//不能修改
cout << (*it) << " ";
}
cout << endl;
}
int main()
{
deque<int> d;
for (int i = 0; i < 10; i++)
{
d.push_front(i); // 前插
}
for (int i = 10; i < 20; i++)
{
d.push_back(i); // 后插
}
print_deque(d);//9 8 7 6 5 4 3 2 1 0 10 11 12 13 14 15 16 17 18 19
//STL提供的排序算法,默认升序
sort(d.begin(), d.end());
print_deque(d);//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
return 0;
}
stack
栈:先进后出
构造函数:
stack<T> stk;//stack采用模板类实现, stack对象的默认构造形式
stack(const stack &stk);//拷贝构造函数
赋值操作:
stack& operator=(const stack &stk);//重载等号操作符
数据存取:
push(elem);//向栈顶添加元素
pop();//从栈顶移除第一个元素
top();//返回栈顶元素
大小操作:
empty();//判断堆栈是否为空
size();//返回栈的大小
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> s;
s.push(1);
s.push(3);
s.push(4);
s.push(5);
cout << "size:" << s.size() << endl;
while (!s.empty())
{
cout << s.top() << endl;
s.pop();
}
cout << "size:" << s.size() << endl;
system("pause");
return 0;
}
queue
队列:先进先出
构造函数:
queue<T> que;//queue采用模板类实现,queue对象的默认构造形式
queue(const queue &que);//拷贝构造函数
赋值操作:
queue& operator=(const queue &que);//重载等号操作符
数据存取:
push(elem);//往队尾添加元素
pop();//从队头移除第一个元素
back();//返回最后一个元素
front();//返回第一个元素
大小操作:
empty();//判断堆栈是否为空
size();//返回栈的大小
list
链表:链式存储。
- 适合插入和删除操作
- 遍历速度没有数组快
- 占用空间比数组大
STL
中的链表是一个双向循环链表
- 动态分配内存,不会造成内存浪费和溢出
- 链表插入和删除不用移动大量元素
- 插入和删除操作不会造成原有的迭代器失效,而在
vector
中不成立
构造函数
list<T> lst;//list采用采用模板类实现,对象的默认构造形式
list(beg,end);//构造函数将[beg,end)区间中的元素拷贝给本身
list(n,elem);//构造函数将n个elem拷贝给本身
list(const list &lst);//拷贝构造函数。
#include <iostream>
#include <list>
using namespace std;
void print_list(const list<int> &li)
{
for (list<int>::const_iterator it = li.begin(); it != li.end(); it++)
{
cout << (*it) << " ";
}
cout << endl;
}
int main()
{
list<int> li;
li.push_back(2);
li.push_back(5);
li.push_back(7);
li.push_back(8);
print_list(li);
list<int> li2(li.begin(), li.end());
print_list(li2);
list<int> li3(10, -1);
print_list(li3);
list<int> li4(li3);
print_list(li4);
system("pause");
return 0;
}
赋值和交换
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身
assign(n, elem);//将n个elem拷贝赋值给本身
list& operator=(const list &lst);//重载等号操作符
swap(lst);//将lst与本身的元素互换
大小操作
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除。
resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除。
插入和删除
push_back(elem);//在容器尾部加入一个元素
pop_back();//删除容器中最后一个元素
push_front(elem);//在容器开头插入一个元素
pop_front()://从容器开头移除第一个元素
insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值.
insert(pos,begend);//在pos位置插入[beg,end)区间的数据,无返回值.
clear();//移除容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置
erase(pos);//删除pos位置的数据,返回下一个数据的位置
remove(elem);//删除容器中所有与elem值匹配的元素
存取
链表只能对首尾元素进行存取,不能随机访问
list
是双向链表,利用迭代器,++,–
front();//返回第一个元素
back();//返回最后一个元素
反转和排序
不支持随机访问的数据结构,不可以使用标准算法,例如sort
sort(li.begin(),li.end())
,不允许的,list
不支持随机访问
reverse();//反转
sort();//排序
#include <iostream>
#include <list>
using namespace std;
void print_list(const list<int> &li)
{
for (list<int>::const_iterator it = li.begin(); it != li.end(); it++)
{
cout << (*it) << " ";
}
cout << endl;
}
bool my_compare(int a, int b)
{
return a > b;
}
int main()
{
// 升序
li.sort();
print_list(li);
// 降序,需要提供一个降序的函数名
li.sort(my_compare);
print_list(li);
}
案例-list-自定义类型排序
自定义数据类型需要指定排序规则
#include <iostream>
#include <string>
#include <list>
using namespace std;
class Person
{
public:
Person(string name, int age, int height)
{
this->m_age = age;
this->m_name = name;
this->m_height = height;
}
string m_name;
int m_age;
int m_height;
};
void print_Person(const list<Person> &lp)
{
for (list<Person>::const_iterator it = lp.begin(); it != lp.end(); it++)
{
cout << "name:" << it->m_name << ",age:" << it->m_age << ",height:" << it->m_height << endl;
}
}
// 指定排序规则
bool person_compare(Person &p1, Person &p2)
{
// 年龄不相等,按照年龄升序
if (p1.m_age != p2.m_age)
{
return p1.m_age < p2.m_age;
}
else // 年龄相等,按照身高降序
{
return p1.m_height > p2.m_height;
}
}
int main()
{
Person p1("张飞", 20, 180);
Person p2("赵云", 29, 174);
Person p3("悟空", 1000, 170);
Person p4("武松", 29, 173);
Person p5("张三", 29, 190);
list<Person> L_person;
L_person.push_back(p1);
L_person.push_back(p2);
L_person.push_back(p3);
L_person.push_back(p4);
L_person.push_back(p5);
print_Person(L_person);
//指定排序规则
L_person.sort(person_compare);
print_Person(L_person);
system("pause");
return 0;
}
set
和multiset
- 所有元素在插入时会自动排序
set
和multi
属于关联式容器,底层结构是二叉树set
不允许有重复的元素multiset
可以有重复的元素
//构造函数
set<T> s;//默认构造
set<T>(const set& s);//拷贝构造
大小
#include <iostream>
#include <set>
using namespace std;
void print_set(set<int> s)
{
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << (*it) << " ";
}
cout << endl;
}
int main()
{
set<int> s;
s.insert(1);
s.insert(4);
s.insert(2);
s.insert(9);
s.insert(2); // 不能重复,不会报错
print_set(s); // 1 2 4 9
cout << "是否为空:" << s.empty() << endl;
cout << "大小:" << s.size() << endl;
set<int> s2;
s2.insert(-1);
s2.insert(-5);
s2.insert(-3);
// 交换
s.swap(s2);
print_set(s); //-5 - 3 - 1
system("pause");
return 0;
}
插入和删除
insert(elem);//在容器中插入元素。
clear();//清除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器.
erase(beg,end);//删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(elem);//删除容器中值为elem的元素
查找和统计
find(key);//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key);//统计key的元素个数
set
和multiset
区别
#include <iostream>
#include <set>
using namespace std;
int main()
{
set<int> s;
pair<set<int>::iterator, bool> ret = s.insert(1); // ret对象,分别返回插入后迭代器的位置和是否插入成功
if (ret.second) // 取第二个参数
{
cout << "插入成功" << endl; // 输出
}
else
{
cout << "插入失败" << endl;
}
ret = s.insert(1); // 第二次插入失败
if (ret.second)
{
cout << "插入成功" << endl;
}
else
{
cout << "插入失败" << endl; // 输出
}
multiset<int> ms;
ms.insert(1);
ms.insert(2);
ms.insert(5);
ms.insert(2);
ms.insert(2);
for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++)
{
cout << (*it) << " ";
}
cout << endl; // 1 2 2 2 5
system("pause");
return 0;
}
pair
队组
pair<type, type> p ( value1, value2 );
pair<type, type> p = make pair( value1, value2 );
#include <iostream>
#include <string>
using namespace std;
int main()
{
// 1
pair<string, int> p1("zhangsan", 17);
cout << p1.first << "," << p1.second << endl;
// 2
pair<string, int> p2 = make_pair("lisi", 10);
cout << p2.first << "," << p2.second << endl;
system("pause");
}
改变set
排序规则
利用仿函数修改排序规则
#include <iostream>
#include <set>
using namespace std;
// 自定义排序规则
class MyCompare
{
public:
// 仿函数
bool operator()(int v1, int v2)
{
return v1 > v2;
}
};
int main()
{
set<int, MyCompare> s;
s.insert(1);
s.insert(4);
s.insert(2);
s.insert(7);
s.insert(9);
for (set<int, MyCompare>::iterator it = s.begin(); it != s.end(); it++)
{
cout << (*it) << " ";
}
cout << endl; // 9 7 4 2 1
system("pause");
return 0;
}
自定义数据类型插入set
,需要指定排序规则
#include <iostream>
#include <set>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
this->m_age = age;
this->m_name = name;
}
string m_name;
int m_age;
};
class personCompare
{
public:
// 仿函数
bool operator()(const Person &p1, const Person &p2)
{
// 按照年龄降序
return p1.m_age > p2.m_age;
}
};
int main()
{
// 自定义数据类型存储在set容器中,需要指定排序规则
Person p1("悟空", 300);
Person p2("刘备", 15);
Person p3("莎莎", 26);
Person p4("王四", 72);
Person p5("呼呼", 50);
// 需要指定仿函数的类
set<Person, personCompare> s_person;
s_person.insert(p1);
s_person.insert(p2);
s_person.insert(p3);
s_person.insert(p4);
s_person.insert(p5);
for (set<Person, personCompare>::iterator it = s_person.begin(); it != s_person.end(); it++)
{
cout << "name:" << it->m_name << ",age:" << it->m_age << endl;
}
system("pause");
return 0;
}
map
和multimap
map
中所有的元素都是pair
,根据键排序,不允许重复
multimap
可以重复
#include <iostream>
#include <map>
#include <string>
using namespace std;
void print_map(map<int, string> &m)
{
for (map<int, string>::iterator it = m.begin(); it != m.end(); it++)
{
cout << (*it).first << " : " << it->second << endl;
}
}
int main()
{
map<int, string> m;
// 插入时会按照key排序
m.insert(pair<int, string>(1, "aaa"));
m.insert(pair<int, string>(4, "bbb"));
m.insert(pair<int, string>(3, "ccc"));
m.insert(pair<int, string>(2, "ddd"));
print_map(m);
// 拷贝构造
map<int, string> m2(m);
print_map(m2);
system("pause");
return 0;
}
大小
size();//大小
empty();//是否为空
swap(m);//交换
插入和删除
insert(elem);//在容器中插入元素
clear();//清除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg, end);//删除区间[beg,end)的所有元素,返回下一个元素的选代器
erase(key);//删除容器中值为kev的元素
// 插入,4种插入方法
map<int, int> m3;
m3.insert(pair<int, int>(1, -1));
m3.insert(make_pair(2, -2));
m3.insert(map<int, int>::value_type(3, -3));
m3[4] = -4;// 但通常是用来取数
for (map<int, int>::iterator it = m3.begin(); it != m3.end(); it++)
{
cout << (*it).first << " : " << it->second << endl;
}
// 删除
m3.erase(3); // 按照key删除
m3.erase(m3.begin()); // 删除迭代器未知的元素
m3.erase(m3.begin(), m3.end()); // 删除迭代器区间的元素
m3.clear();
查找和统计
find(key);//查找key是否存在,若存在,返回该键的元素的迭代器; 若不存在,返回set.end();
count(key);//统i计key的元素个数
排序-修改排序规则
// 自定义排序规则
class MyCompare
{
public:
bool operator()(int v1, int v2)
{
return v1 > v2;
}
};
int main()
{
// 自定义排序规则-降序
map<int, int, MyCompare> m_c;
m_c.insert(make_pair(1, -1));
m_c.insert(make_pair(3, -3));
m_c.insert(make_pair(2, -2));
m_c.insert(make_pair(4, -4));
for (map<int, int, MyCompare>::iterator it = m_c.begin(); it != m_c.end(); it++)
{
cout << "key=" << it->first << ",value=" << it->second << endl;
}
}
案例-员工分组
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <ctime>
using namespace std;
// 10个员工,随机分配到 测试,研发,美术部门,随机分配工资
// 分别显示每个部门的员工信息
#define CESHI 0
#define YANFA 1
#define MEISHU 2
class Employee
{
public:
Employee(string name, int salary)
{
this->m_name = name;
this->m_salary = salary;
}
string m_name;
int m_salary;
};
// 创建10名员工
void create_employee(vector<Employee> &v_emp)
{
string name_arr = "ABCDEFGHIJ";
for (int i = 0; i < 10; i++)
{
int sal = rand() % 10000 + 10000;
Employee emp(name_arr.substr(i, 1), sal);
v_emp.push_back(emp);
}
}
// 打印员工
void print_employee(const vector<Employee> &v_emp)
{
for (vector<Employee>::const_iterator it = v_emp.begin(); it != v_emp.end(); it++)
{
cout << "name:" << it->m_name << ",salary:" << it->m_salary << endl;
}
}
// 分组到不同部门
void group_department(const vector<Employee> &v_emp, multimap<int, Employee> &m_emp)
{
for (vector<Employee>::const_iterator it = v_emp.begin(); it != v_emp.end(); it++)
{
// 给当前遍历的员工随机分配一个部门
int dep_id = rand() % 3; // 0,1,2
m_emp.insert(make_pair(dep_id, (*it))); // 部门编号-员工
}
}
// 打印不同部门的员工信息
void print_department_employee(const multimap<int, Employee> &m_emp)
{
cout << "测试:" << endl;
multimap<int, Employee>::const_iterator start_ceshi = m_emp.find(CESHI); // 返回迭代器的位置
int count_ceshi = m_emp.count(CESHI); // 测试部门的员工数量
for (int index = 0; start_ceshi != m_emp.end() && index < count_ceshi; start_ceshi++, index++)
{
cout << "name:" << start_ceshi->second.m_name << ",salary:" << start_ceshi->second.m_salary << endl;
}
cout << "研发:" << endl;
multimap<int, Employee>::const_iterator start_yanfa = m_emp.find(YANFA); // 返回迭代器的位置
int count_yanfa = m_emp.count(YANFA); // 研发部门的员工数量
for (int index = 0; start_yanfa != m_emp.end() && index < count_yanfa; start_yanfa++, index++)
{
cout << "name:" << start_yanfa->second.m_name << ",salary:" << start_yanfa->second.m_salary << endl;
}
cout << "美术:" << endl;
multimap<int, Employee>::const_iterator start_meishu = m_emp.find(MEISHU); // 返回迭代器的位置
int count_meishu = m_emp.count(MEISHU); // 美术部门的员工数量
for (int index = 0; start_meishu != m_emp.end() && index < count_meishu; start_meishu++, index++)
{
cout << "name:" << start_meishu->second.m_name << ",salary:" << start_meishu->second.m_salary << endl;
}
}
int main()
{
// 利用系统时间设置随机数
srand((unsigned int)time(NULL));
vector<Employee> v_emp;
create_employee(v_emp);
print_employee(v_emp);
// 分组到不同部门
multimap<int, Employee> m_emp;
group_department(v_emp, m_emp);
print_department_employee(m_emp);
system("pause");
return 0;
}