c++核心编程

内存分区模型

C++程序在执行时,将内存大方向划分为4个区域

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的
  • 全局区:存放全局变量和静态变量以及常量
  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

 内存四区意义:

  • 不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程

 程序运行前

运行前代码放在磁盘里,运行之后各种变量才会进入内存里,也就是栈区和堆区

程序运行后 

栈区:

  • 由编译器自动分配释放,存放函数的参数值(形参),局部变量等(出了代码块,局部变量释放)
  • 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

堆区:

  • 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
  • 在C++中主要利用new在堆区开辟内存 

new操作符 

  • C++中利用new操作符在堆区开辟数据堆区开辟的数据,由程序员手动开辟,手动释放
  • 释放利用操作符 delete
  • 语法:new 数据类型
  • 利用new创建的数据,会返回该数据对应的类型的指针

 引用

  • 给变量起别名(是同一块地址)(本质是指针常量)指针常量和常量指针-CSDN博客
  • 语法:数据类型 &别名=原名

    int a=10;

    int &b=a;
    b=20;
    cout<<a<<endl; //20

 引用注意事项

  • 引用必须初始化,引用一旦初始化后,就不可以更改了
  • 引用本质是指针常量,指针常量本质上一个常量,指针用来说明常量的类型,表示该常量是一个指针类型的常量。
  • 在指针常量中,指针自身的值是一个常量,不可改变,始终指向同一个地址。在定义的同时必须初始化。

引用做函数参数 

void func(int &a,int &b)//起别名,是同一块地址
{
	int temp=a;
	a=b;
	b=temp;
}
int main()
{
	int a=10,b=20;
	func(a,b);
	cout<<a<<" "<<b;
 } 
  • 总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单

 引用做函数返回值

int& text01()
{
	int a=10;//局部变量存放在四区的栈区,离开代码块销毁
	return a;//以引用的形式返回,相当于返回这块地址,已被销毁 
}
int& text02()
{
	static int a = 10;//静态变量,存放在全局区,全局区上的数据在程序结束后系统释放 
 } 
int main()
{
	int &ref = text01();
	cout<<"ref="<<ref<<endl;//ref=10   ref=0 
	cout<<"ref="<<ref<<endl;//第一次编译器保留,后正常销毁
	int &ref2 = text02();
	cout<<"ref="<<ref<<endl;
	cout<<"ref="<<ref<<endl;
	//引用的函数调用可以作为左值 
	rext02()=1000;
	cout<<"ref2="<<ref2<<endl;//此时test02(),ref,a都变成同等地位的20的一个代号 
	cout<<"ref2="<<ref2<<endl;
	 
 }

  • ref2  是 test02()的别名      test02()是a的别名    所以    ref 2=test02()=a
  • 因此当test02()被重新赋值时,ref2的值也会改变!
     

引用的本质 

本质:引用的本质在c++内部实现是一个指针常量

  • 指针需要开辟新的空间,但是引用不需要
  •  引用不支持取地址操作,取的地址是原数据的地址
  • 而指针取的是指针的地址

常量引用 

作用:用来修饰形参,防止误操作

  • 形参改变实参这种情况只可能发生在地址传递或引用传递之中。
  • 常量引用=常量指针常量
void showValue(const int &val)
{
	//val=1000;
 } 
int main()
{ 
	int a=10;
	//int &ref=10;//引用必须引一块合法的内存空间,10是常量(字面量)
	const int & ref=10;//加上const修饰,编译器将代码修改
	//相当于int temp=10;  const int &ref=temp;(给10一块地址) 
	//ref=20; 加入const后变为只读,不可以修改
	showValue(a);	 
 }

 函数提高

函数默认参数

  • 在C++中,函数的形参列表中的形参是可以有默认值的。
  • 语法:返回值类型   函数名  (参数=默认值)
  • 默认参数必须在参数列表的末尾
  • 如果函数声明有默认参数,函数实现就不能有默认参数
int func(int a,int b=20,int c=30)
//如果我们自己传入数据,就用自己的数据,如果没有,那么用默认值 
{
	return a+b+c;
 } 
int main()
{
	cout<<func(10,30)<<endl;
}

函数占位参数

  • C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
  • 语法:返回值类型 函数名(数据类型){}
  • 在现阶段函数的占位参数存在意义不大,但是后面的课程中会用到该技术
void func(int a,int)//这个int就是函数占位,目前用不到 
{
	cout<<"hello world"<<endl;
}
void func2(int a,int =10)//可以有默认参数 
{
	
}
int main()
{
	func(10,10);
	func2(10); 
}

 函数重载

函数重载概括

作用:函数名可以相同,提高复用性

函数重载满足条件:

  • 同一个作用域下
  • 函数名称相同
  • 函数参数类型不同 或者 个数不同 或者 顺序不同
  • 注意:函数的返回值不可以作为函数重载的条件
void func(){
	cout<<"调用1 "<<endl; 
}
void func(int a){
	cout<<"调用2 "<<a<<endl; 
}
void func(double a){
	cout<<"调用3 "<<a<<endl; 
}
void func(double a,int a);
void func(int a,double a);
int main(){
	func();//调用1 
	func(2);//调用2 
	func(2.5);//调用3 
	return 0;
}

函数重载注意事项

  • 引用作为重载条件
  • 函数重载碰到函数默认参数
void func(int &a){ 
	cout<<1<<endl;
}
void func(const int &a){
	cout<<2<<endl;
}
void func2(int a,int b){
	cout<<3<<endl;
}
void func2(int a){
	cout<<4<<endl;
}
void func2(int a,int b=10){
	cout<<5<<endl;
}
int main(){
	int a=10;
	func(a);//将变量a传过去,只能用1接收 
	func(10);//将常量a传过去,相当于常数引用,只能用2接收 
	func2(10);//可以传给4,也可以传给5,存在二义性
	 
} 
  •  本质是需要让计算机可以区分两个函数

 类和对象

  • C++面向对象的三大特性为: 封装、继承、多态
  • C++认为万事万物都皆为对象对象上有其属性和行为

例如:

  • 人可以作为对象,属性有姓名、年龄、身高、体重....行为有走、跑、跳、吃饭、唱歌..
  • 车也可以作为对象,属性有轮胎、方向盘、车灯…,行为有载人、放音乐、放空调...
  • 具有相同性质的对象,我们可以抽象称为类,人属于人类,车属于车类

封装

封装是C++面向对象三大特性之一

封装的意义:

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制

封装意义一:

  • 在设计类的时候,属性和行为写在一起,表现事物
  • 语法:class 类名{ 访问权限:属性 /行为 }; 
//设计一个圆类,求圆的周长
//公式:2*PI*半径
//class代表设计一个类,后跟类名称 
class Circle{
	//访问权限:公共权限 
public: 
	//属性:半径
	int m_r;
	//行为:获取圆的周长
	double calculateZC(){
		return 2*3.14*m_r;
	} 
}; 
int main(){
	Circle cl;//实例化(通过一个类,创建一个对象) 
	cl.m_r=10;//给属性赋值 
	cout<<"圆的周长"<<cl.calculateZC()<<endl;//调用类的行为 
}
class Student{
public:
	string m_Name;
	int m_id;
public://可不写
	void showStudent(){
		cout<<"姓名: "<<m_Name<<"学号:  "<<m_id<<endl; 
	}
	//给名字赋值
	void setName(string name){
		m_Name=name;	
	}
	//给学号赋值
	void setId(int id){
		m_id=id;
		}	
};
int main(){
	//创建一个具体学生实例化对象
	Student s1;//给s1对象 进行属性赋值操作
//	sl.m Name ="张三";
//	sl.m Id = 1;
	s1.setName("张三");
	s1.setId(1); 
	//显示学生信息
	s1.showStudent();

	Student s2;
	s2.m_Name ="李四";
	s2.m_id = 2;
	s2.showStudent();
}
  • 类中的属性和行为 我们统一称为 成员
  • 属性   成员属性 成员变量
  • 行为   成员函数 成员方法
  • 尽量叫方法,类中的函数全部叫作方法,方法是面向对象的东西

封装意义二:

  • 类在设计时,可以把属性和行为放在不同的权限下,加以控制访问权限有三种:
  • 1. public公共权限         类外类内都可以访问 
  • 2.protected 保护权限   类外不可以访问  继承的对象可以访问保护内容
  • 3. private私有权限       类外不可以访问  继承的对象也不能访问内容
struct和class区别

在C++中 struct和class区别在于默认的访问权限不同

  • struct 默认权限为 公共 public
  • class 默认权限为 私有 private
  • 注:c++中的strust结构体获得类的大部分功能,区别在与默认权限
封装读写权限操作
class Person
{
//	私有类内传给公共再传给类外
public://公有 
	//设置姓名
	void setName(string name)
	{
		m_Name=name;
	}
	//获取姓名
	string getName()
	{
		return m_Name;	
	}
	//获取年龄
	int get_Age()
	{
		return m_Age;
	}
	//设置偶像
	void  setIdol(string idol)
	{
		m_Idol=idol;
	}
private:
	string m_Name;//姓名 可读可写
	int m_Age=18;//年龄 只读
	string m_Idol;//偶像 只写
	
};
int main(){
	Person p;
	//姓名设置
	p.setName("张三");
	cout<<"姓名是"<<p.getName()<<endl; 
	cout<<"年龄是"<<p.get_Age(); 
	p.setIdol("周深"); 
	
}
  •  可以自己控制读写权限
  • 对于写可以检测数据有效性
  • 为了让类的使用者只能通过设计者提供的接口去修改类的属性
	//设置年龄(0~150)
	void setAge(int age)
	{
		if(age<0||age>150)
		{
			cout<<"输入有误,赋值失败"<<endl;
			return; 
		}
		m_Age=age;
	 } 
练习案例
  • 设计立方体类(Cube)
  • 求出立方体的面积和体积
  • 分别用全局函数和成员函数判断两个立方体是否相等
//立方体类设计
//创建立方体类
//设计属性
//设计行为 获取立方体的面积和体积
//分别利用全局函数和成员函数 判断两个立方体是否相等 
class Cube{
	public:
	//设置长宽高 
	void setS(int l,int w,int h){
		m_W=w;
		m_H=h;
		m_L=l;
	}
	//获取长宽高 
	int getH(){
		return m_H;
	}
	int getL(){
		return m_L;
	}
	int getW(){
		return m_W;
	}
	//获取立方体面积
	int calculateS(){
		return 2*m_L*m_W+2*m_H*m_W+2*m_L*m_H;
	}
	//获取体积
	int  calculateV(){
		return m_L*m_W*m_H;
	}
	//类内比较两个立方体
	//比较外界立方体和类内立方体 
	bool isSameByClass(Cube &c){
		if(m_L==c.getH()&&m_W==c.getL()&&m_H==c.getW())	
		return true;
	} 
	private:
		int m_L;
		int m_W;
		int m_H;
};
bool isSame(Cube &cl,Cube &c2){//引用是为了节省空间,仅判断不需要实参 
	if(cl.getH()==c2.getH()&&cl.getL()==c2.getL()&&cl.getW()==c2.getW())
	{
		return true;
	}
}
int main()
{
	Cube cl;
	cl.setS(10,10,10);
	cout<<cl.calculateS()<<endl;
	cout<<cl.calculateV()<<endl;
	Cube c2;
	c2.setS(10,10,10);
	if(isSame(cl,c2)){
		cout<<"相等"<<endl; 
	}
	if(cl.isSameByClass(c2)){
		cout<<"相等"<<endl; 
	 } 
}
#include<iostream>
using namespace std;
//点和圆的关系
//点类
class Point {
public:
	void setX(int x){
		m_x = x;
	}
	int getX(){
		return m_x;
	}
	void setY(int y){
		m_y = y;
	}
	int getY(){
		return m_y;
	}
private:
	int m_x;
	int m_y;
};
//圆类
class Circle{
public:
	void setR(int r){
		m_r = r;
	}
	int getR(){
		return m_r;
	}
	void setCenter(Point center){
		m_Center = center;
	}
	Point getCenter(){
		return m_Center;
	}
private:
	int m_r;
	Point m_Center;//圆心(圆心和点都属于点类)
};
void isInCircle(Circle &c, Point &p){
	//计算两点之间距离(平方)
	int distance =
		(c.getCenter().getX() - p.getX())*(c.getCenter().getX() - p.getX()) +
		(c.getCenter().getY() - p.getY())*(c.getCenter().getY() - p.getY());
	//计算半径(平方)
	int rDistance = c.getR()*c.getR();
	//判断关系
	if (distance == rDistance)
	{
		cout << "点在圆上" << endl;
	}
	else if (distance > rDistance) {
		cout << "点在圆外" << endl;
	}
	else {
		cout << "点在圆内" << endl;
	}

}
int main() {
	//创建圆
	Circle c;
	c.setR(10);
	Point center;
	center.setX(10);
	center.setY(0);
	c.setCenter(center);
	//创建点
	Point p;
	p.setX(10);
	p.setY(10);
	//判断关系
	isInCircle(c, p);
}
类的分装 
//Cricle.h
#pragma once
#include<iostream>
#include"point.h"
//圆类
class Circle {
public:
	void setR(int r);
	int getR();
	void setCenter(Point center);
	Point getCenter();
private:
	int m_r;
	//在类中,可以让另一个类作为成员
	Point m_Center;//圆心(圆心和点都属于点类)
};
//cricle.cpp
#include"Circle.h"
//圆类
	void Circle::setR(int r) {
		m_r = r;
	}
	int Circle::getR() {
		return m_r;
	}
	void Circle::setCenter(Point center) {
		m_Center = center;
	}
	Point Circle::getCenter() {
		return m_Center;
	}

对象的初始化和清理

  • 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全
  • C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置。
构造函数和析构函数

对象的初始化和清理也是两个非常重要的安全问题

  • ​ 一个对象或者变量没有初始状态,对其使用后果是未知
  • ​ 同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题

c++利用了构造函数析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。

对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供。编译器提供的构造函数和析构函数是空实现。

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

构造函数语法:类名(){}

  1. 构造函数,没有返回值也不写void
  2. 函数名称与类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数语法: ~类名(){}

  1. 析构函数,没有返回值也不写void
  2. 函数名称与类名相同,在名称前加上符号 ~
  3. 析构函数不可以有参数,因此不可以发生重载
  4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
class Person {
public:
	//1.构造函数  进行初始化操作
	Person()
	{
		cout << "构造" << endl;
	}
	//析构函数 进行清理的操作
	~Person()
	{
		cout << "析构" << endl;
	}

};
//构造和析构必须有,不写,编译器自己提供空的实现
void test01() {
	Person p;//在栈上的数据,text01执行完,释放这个对象
}
int main(){
	test01();//打印构造
	return 0;//打印析构
}
构造函数的分类及调用

两种分类方法:

  • 按参数分为: 有参构造和无参构造
  • 按类型分为: 普通构造和拷贝构造

三种调用方式:

  • 括号法
  • 显示法
  • 隐式转换法
class Person {
public:
	//无参构造(默认构造)
	Person() {
		cout << "无参构造" << endl;
	}
	//有参构造
	Person(int a,int b) {
		age = a;
		i = b;
		cout << "有参构造" << endl;
	}
	//拷贝构造函数,构造函数的作用就是初始化属性,
	//那么拷贝构造相当于传进来一个有属性的类,
	//来初始化我这个类的属性
	Person(const Person &p){//引用防止递归调用,const防止修改
		//将传入的人身上的属性,拷贝到我身上
		cout << "拷贝构造" << endl;
		age = p.age;
	}
	~Person()
	{
		cout << "无参析构" << endl;
	}
	int age;
	int i;
}; 
void test01()
{
	//括号法
	Person p1;//无参(默认)构造函数调用
	Person p2(10,2);
	Person p3(p2);
	cout << "p2 " << p2.age <<p2.i<< endl;
	cout << "p3 " << p3.age <<p3.i<< endl;

	//显示法
	Person p2 = Person(10,2);
    Person p3 = Person(p2);
	//右边是匿名对象,左边是对象的名
	//特点:当前行执行结束后,系统会立即回收(析构)掉匿名对象
	Person(10, 2);//匿名对象
	

	//隐形转换法
	Person p4 = { 10,2 };//相当于Person p4=Person(10,2);
	Person p5 = p4;
}
int main(){
	test01();
}

 注意事项:

  • 调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
  • 不要利用拷贝构造函数,初始化匿名对象 编译器认为是对象的声明,导致重定义
    相当于显示法的 Person p3 =Person(p3);
  • 拷贝构造虽然是引用,但是对象的地址并不相同
拷贝构造函数调用时机 

C++中拷贝构造函数调用时机通常有三种情况

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象
class Person {
public:
	Person() {
		cout << "无参构造" << endl;
	}
	Person(int age){
		cout << "有参构造" << endl;
		m_age = age;
	}
	Person(const Person &p){
		cout << "拷贝构造" << endl;
		m_age = p.m_age;
	}
	~Person(){
		cout << "无参析构" << endl;
	}
	int m_age;
};
void test01() {
	Person p1(20);
	Person p2(p1);
}
void doWork(Person p_1){
	
}
//值传递的方式给函数参数传值
void text02(){
	Person p;
	doWork(p);//相当于隐形转换Person p_1=p; 会进行拷贝构造
}
//值传递返回局部对象
Person dowork2() {
	Person p1;
	cout << (int*)&p1<<endl;
	return p1;//进行了拷贝构造和无参析构(复制一份的同时销毁掉)
}
void  test03() {
	Person p = dowork2();
	cout << (int*)&p << endl;
}

int main()
{
	//test01();
	//text02();
	//test03();
}
 构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数(对属性进行值拷贝)

构造函数调用规则如下:

  • 如果用户定义有参构造函数,c++不在提供默认无参构造(Person p失效),但是会提供默认拷贝构造(可以实现拷贝功能)
  • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
  • 拷贝>有参>无参  (有前就不提供后面)
深拷贝和浅拷贝 
  • 浅拷贝:简单的赋值拷贝操作(编译器默认使用)
  • 深拷贝:在堆区重新申请空间,进行拷贝操作
class Person{
public:
	Person(){
		cout << "无参构造" << endl;
	}
	Person(int age,int height) {
		m_age = age;
		
		m_height = new int(height);
		cout << "有参构造" << endl;
	}
	~Person() {//将堆区开辟数据做释放操作
		if (m_height != NULL) {
			delete m_height;
			m_height = NULL;
		}
		cout << "无参析构" << endl;
	}
	Person(const Person &p) {
		cout << "拷贝构造函数" << endl;
		m_age = p.m_age;
		//m_height = p.m_height; 搬一起默认实现的就是这行代码
		//深拷贝操作
		m_height = new int(*p.m_height);
	}
	int m_age;
	int *m_height;

};
void text01() {
	Person p1(18,160);
	cout << "p1年龄为" << p1.m_age <<"身高为"<<*p1.m_height<< endl;
	Person p2(p1);
	cout << "p2的年龄为" << p2.m_age << endl;
} 
int main() {
	text01();
}
  • 假设需要将数据建立的堆区,那需要用指针的形式创造 (因为堆区的数据可以手动释放,在函数体外也可以使用)
  • 那如果拷贝构造的话需要使用new创建数据(利用new创建的数据,会返回该数据对应的类型的指针)
  • 编译器默认提供拷贝构造函数,进行浅拷贝操作,将p1和p2的数据完全一致的拷贝过来,导致对象p2中的指针变量和p1的指针一样,指向和p1相同的堆区内存空间(即两个指针指向同一块堆区地址)m_height = p.m_height; 
  • 析构的时候会发生同一块堆区反复释放
  • 所以需要手动拷贝构造,m_height = new int(*p.m_height); 即深拷贝

STL

STL初识

概念

  • STL(Standard Template Library,标准模板库)
  • STL 从广义上分为: 容器(container) 算法(algorithm)选代器(iterator)
  • 容器和算法之间通过迭代器进行无缝连接。
  • STL 几乎所有的代码都采用了模板类或者模板函数

 STL六大组件

STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

  • 1.容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据
  • 2.算法:各种常用的算法,如sort、find、copy、for_each等
  • 3.迭代器:扮演了容器与算法之间的胶合剂。
  • 4. 仿函数:行为类似函数,可作为算法的某种策略。
  • 5.适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
  • 6.空间配置器:负责空间的配置与管理,

 STL中容器、算法、迭代器

容器:置物之所也

STL容器就是将运用最广泛的一些数据结构实现出来

常用的数据结构:数组, 链表,树, 栈, 队列, 集合, 映射表 等

  • 这些容器分为序列式容器关联式容器两种:
    • ​ 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
    • 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

**算法:**问题之解法也

有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)

  • 算法分为:质变算法非质变算法
    • 质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
    • 非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等

**迭代器:**容器和算法之间粘合剂

提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。

每个容器都有自己专属的迭代器

迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针

迭代器种类:

常用的为双向迭代器随机访问迭代器 

容器算法迭代器初识 

 vector存放内置数据类型
  • 容器: vector
  • 算法: for_each
  • 迭代器: vector<int>::iterator
#include<bits/stdc++.h>
using namespace std;
void myPrint(int value) {
	cout << value << endl;
}
//vector容器存放内置数据结构
void test01() {
	//创建一个vector容器,数组
	vector<int> v;
	//向容器中插入数据
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);
	//通过迭代器访问容器中的数据
	//获取容器的开始迭代器,指向第一个元素
	vector<int>::iterator itBegin = v.begin();
	//获取容器的结束迭代器,指向最后一个元素的下一位置
	vector<int>::iterator itEnd = v.end();
	//第一种遍历方式:几乎不用
	while(itBegin != itEnd) {
	cout <<*itBegin<< endl;
	itBegin++;
	}
	//第二种遍历方式
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << endl;
	}
	//第三种遍历方式,利用STL提供的遍历算法
	for_each(v.begin(), v.end(), myPrint);
	//第四种遍历方式,利用数组下标访问元素
	for (int i = 0; i < v.size(); i++) {
		cout << v[i] << endl;
	}
}
int main() {
	test01();
	return 0;
}
 vector存放自定义数据类型
#include<bits/stdc++.h>
using namespace std;
//vector容器中存放自定义数据类型
class Person {
public:
	Person(string name, int age) {
		this->name = name;
		this->age = age;
	}
	string name;
	int age;
};
void text01() {
	vector<Person> v;
	v.push_back(Person("Alice", 25));
	v.push_back(Person("Bob", 30));
	v.push_back(Person("Charlie", 35));
	//使用下标遍历vector容器
	for (int i = 0; i < v.size(); i++) {
		cout << "姓名:" << v[i].name << " 年龄:" << v[i].age << endl;
	}
	//使用迭代器遍历vector容器
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
		cout << "姓名:" << it->name << " 年龄:" << it->age << endl;
	}
}
int main() {
	text01();
};
vector容器嵌套容器
#include <bits/stdc++.h>
using namespace std;
//容器嵌套容器
void test01() {
	vector<vector<int>> v;
	//创建小容器
	vector<int> v1;
	vector<int> v2;
	vector<int> v3;
	vector<int> v4;
	//向小容器中添加元素
	for (int i = 0; i < 4; i++) {
		v1.push_back(i);
		v2.push_back(i + 1);
		v3.push_back(i + 2);
		v4.push_back(i + 3);
	}
	//向大容器中添加小容器
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	v.push_back(v4);
	//利用下标访问元素输出大容器
	for (int i = 0; i < v.size(); i++) {
		for (int j = 0; j < v[i].size(); j++) {
			cout << v[i][j] << " ";
		}
		cout << endl;

	}
	//利用迭代器访问元素输出大容器
	for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {
		for (vector<int>::iterator it2 = (*it).begin(); it2 != (*it).end(); it2++) {
			cout << *it2 << " ";
		}
		cout << endl;
	}
}
int main() {
		test01();
		return 0;
	}

 String基本概念

本质:

  • string是C++风格的字符串,而string本质上是一个类

string和char * 区别:

  • char * 是一个指针。
  • string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器。
  • string和char类型数组比起来末尾多了一个\0。

特点:

  • string 类内部封装了很多成员方法
  • 例如:查找find,拷贝copy,删除delete 替换replace,插入insert
  • string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
string构造函数

构造函数原型:

  • string(); //创建一个空的字符串 例如: string str;
  • string(const char* s); //使用字符串s初始化
  • string(const string& str); //使用一个string对象初始化另一个string对象
  • string(int n, char c); //使用n个字符c初始化
    #include <bits/stdc++.h>
    using namespace std;
    void test01() {
    	//这是字符串对象,编译器会在堆上分配内存,并将其地址赋值给s1
    	string s1;
    	//这是字符串字面值常量,编译器会将其直接内联到程序中,不会产生新的内存分配
    	const char* s2 = "Hello World";
    	//这是字符数组,编译器会在栈上分配内存,并将其地址赋值给s3
    	char s3[] = "Hello World";
    	string s4(s2); //利用字符串初始化
    	cout << s4 << endl;
    	string s5(s3); //利用字符数组初始化
    	cout << s5 << endl;
    	string s6(10, 'a'); //利用指定长度和字符初始化
    	cout << s6 << endl;
    	string s7(s4); //利用字符串拷贝初始化
    	cout << s7 << endl;
    	string s8(s4, 3, 5); //利用字符串和范围初始化
    	cout << s8 << endl;//lo Wo
    }
    int main() {
    	test01();
    	return 0;
    }
    

arr[0] 的赋值操作不是在尝试修改原始的字符串字面值 “aaa”,而是将 arr[0] 的指针从原来指向 “aaa” 的地址改为指向 “baa” 的地址。这是合法的操作,因为它只涉及指针本身的改变,而不涉及尝试修改常量数据。

总结来说,arr[0] = “baa”; 可以进行赋值,因为它仅仅修改了指针的指向,而不是尝试修改字符串字面值本身的内容。

 String赋值操作

功能描述:

  • 给string字符串进行赋值
#include <bits/stdc++.h>
using namespace std;
void test01() {
	string str1="Hello World";//把字符串常量(类型const char*)赋值给str1
	cout<<str1<<endl;
	string str2=str1;//把str1的副本赋值给str2
	cout<<str2<<endl;
	//string str3 = 'a';//不能在声明的时候直接用单个字符对字符串进行赋值
	string str3;
	str3 = 'a';//可以用单个字符对字符串进行赋值
	cout<<str3<<endl;
	string str4;
	str4.assign("Hello C++");//用assign函数赋值
	cout<<str4<<endl;
	string str5;
	str5.assign("hello", 3);//前3个字符赋值hel
	cout<<str5<<endl;
	string str6;
	str6.assign(str1, 3, 5);//从str1下标3开始复制5个字符lo wo
	cout<<str6<<endl;
}	
int main() {
	test01();
    return 0;	
}

​ string的赋值方式很多,= 这种方式是比较实用的

 string字符串拼接

功能描述:

  • 实现在字符串末尾拼接字符串
#include<bits/stdc++.h>
using namespace std;
//字符串拼接
void test01()
{
	string str1 = "我";
	str1 += "爱玩游戏";
	cout << "str1 = " << str1 << endl;
	str1 += ':';
	cout << "str1 = " << str1 << endl;
	string str2 = "LOL DNF";
	str1 += str2;
	cout << "str1 = " << str1 << endl;
	string str3 = "I";
	str3.append(" love ");
	str3.append("game abcde", 4);
	str3.append(str2);
	str3.append(str2, 4, 3); // 从下标4位置开始 ,截取3个字符,拼接到字符串末尾
	cout << "str3 = " << str3 << endl;
}
int main() {
	test01();
	return 0;
}
string增删查改

功能描述:

  • 查找:查找指定字符串是否存在
  • 替换:在指定的位置替换字符串
#include <bits/stdc++.h>
using namespace std;
//查找
void test01() {
    string str1 = "abcdefgcd";
    //返回cd的位置(首次出现的下标),如果没有找到,返回-1
    cout << str1.find("cd");
    //返回cd的位置(最后出现的下标),如果没有找到,返回-1
    cout << str1.rfind("cd");
}
//替换
void test02() {
    string str1 = "abcdefgcd";
    //替换cd为ef
    str1.replace(str1.find("cd"), 2, "ef");
    cout << str1;
    //替换下标1-3位为1111
    str1.replace(1, 3, "1111");//a1111efgcd
    cout << str1;
}
//插入
void test03() {
    string str1 = "abcdefgcd";
    //插入1111
    str1.insert(1, "1111");//a1111bcdefgcd
    cout << str1<<endl;
    //在cd后面插入ef
    str1.insert(str1.find("cd") + 2, "ef");
    cout << str1<<endl;
}
//删除
void test04() {
    string str1 = "abcdefgcd";
    //删除ab
    str1.erase(0, 2);//从0开始删除2个字符,即ab
    cout << str1;
    //删除cd
    str1.erase(str1.find("cd"), 2);
    cout << str1;
}
int main() {
    //test01();
    //test02();
    //test03();
    //test04();
    return 0;
}
  • find查找是从左往后,rfind从右往左
  • find找到字符串后返回查找的第一个字符位置,找不到返回-1
  • replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串
  • 插入和删除的起始下标都是从0开始
string字符串比较

功能描述:

  • 字符串之间的比较

比较方式:

  • 字符串比较是按字符的ASCII码进行对比
    • = 返回 0
    • > 返回 1
    • < 返回 -1
//字符串比较
void test01()
{
	string s1 = "hello";
	string s2 = "aello";
	int ret = s1.compare(s2);
	if (ret == 0) {
		cout << "s1 等于 s2" << endl;
	}
	else if (ret > 0)
	{
		cout << "s1 大于 s2" << endl;
	}
	else
	{
		cout << "s1 小于 s2" << endl;
	}
}

int main() {
	test01();
	return 0;
}

字符串对比主要是用于比较两个字符串是否相等,判断谁大谁小的意义并不是很大

string字符存取

string中单个字符存取方式有两种

  • char& operator[](int n);  //通过[]方式取字符
  • char& at(int n);  //通过at方法获取字符
#include<bits/stdc++.h>
using namespace std;
void test01()
{
	string str = "hello world";
	for (int i = 0; i < str.size(); i++)
	{
		cout << str[i] << " ";
	}
	cout << endl;
	for (int i = 0; i < str.size(); i++)
	{
		cout << str.at(i) << " ";
	}
	cout << endl;
	//字符修改
	str[0] = 'x';
	str.at(1) = 'x';
	cout << str << endl;
}

int main() {
	test01();
	return 0;
}
string子串

功能描述:

  • 从字符串中获取想要的子串
#include<bits/stdc++.h>
using namespace std;
//子串
void test01()
{

	string str = "abcdefg";
	//从第1个字符开始,截取3个字符
	string subStr = str.substr(1, 3);
	cout << "subStr = " << subStr << endl;
	string email = "hello@sina.com";
	int pos = email.find("@");
	//从0开始,截取到@的位置
	string username = email.substr(0, pos);
	cout << "username: " << username << endl;
	//从@的位置开始,截取到结尾
	string domain = email.substr(pos + 1);
	cout << "domain: " << domain << endl;
}

int main() {
	test01();
	return 0;
}

vector容器

vector基本概念

功能:

  • vector数据结构和数组非常相似,也称为单端数组

vector与普通数组区别:

  • 不同之处在于数组是静态空间,而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<bits/stdc++.h>
using namespace std;
void printVector(vector<int> v)
{
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
}
void test01()
{
	vector<int> v1; //无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);
	vector<int> v2(v1.begin(), v1.end());//将v1的元素拷贝到v2
	printVector(v2);
	vector<int> v3(10, 100);//构造函数,将10个100填充到v3
	printVector(v3);
	vector<int> v4(v3);//拷贝构造
	printVector(v4);
}

int main() {
	test01();
	return 0;
}
vector赋值操作

功能描述:

  • 给vector容器进行赋值

函数原型:

  • vector& operator=(const vector &vec);//重载等号操作符

  • assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。

  • assign(n, elem); //将n个elem拷贝赋值给本身。

#include<bits/stdc++.h>
using namespace std;
void print(vector<int> v)
{
	for(int i=0;i<v.size();i++)
		cout<<v[i]<<" ";
	cout<<endl;
}
void test01(){
	//直接赋值
	vector<int> v0={1,2,3,4,5};
	print(v0);
	//输入赋值
	vector<int> v6;
	for (int i = 0; i < 10; i++){
		int x;
		cin >> x;
		v6.push_back(x);
	}
	print(v6);
	vector<int> v1;
	for(int i=0;i<10;i++)
		v1.push_back(i);
	print(v1);
	//拷贝构造函数
	vector<int> v2=v1;
	print(v2);
	vector<int> v3(v1);
	print(v3);
	//assign函数
	vector<int> v4;
	v4.assign(v1.begin(),v1.end());
	print(v4);
	//n个element的构造函数
	vector<int> v5(10,1);
	print(v5);
}

int main() {
	test01();
	return 0;
}
vector容量和大小

功能描述:

  • 对vector容器的容量和大小操作

函数原型:

  • empty();  //判断容器是否为空

  • capacity(); //容器的容量

  • size(); //返回容器中元素的个数

  • resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。

    • ​ //如果容器变短,则末尾超出容器长度的元素被删除。

  • resize(int num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。

    • ​ //如果容器变短,则末尾超出容器长度的元素被删除

#include<bits/stdc++.h>
using namespace std;
void print(vector<int> v) {
	for (int i = 0; i < v.size(); i++) {
		cout << v[i] << " ";
	}
	cout << endl;
}
void test01() {
	vector<int> v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	print(v1);
	if (v1.empty()) {
		cout << "v1为空"<<endl;
	}
	else {
		cout << "v1不为空"<<endl;
		cout << "v1的容量为:" << v1.capacity() << endl;
		cout << "v1的大小为:" << v1.size() << endl;
	}
	//重新指定大小
	v1.resize(15);//若指定大小大于当前大小则会扩大
	//v1.resize(15, 100);
	print(v1);//默认填充0,若指定参数则填充指定值 
	v1.resize(5);//若小于当前大小则会缩小
	print(v1);
}
int main() {
	test01();
	return 0;
}	

总结:

  • 判断是否为空 --- empty
  • 返回元素个数 --- size
  • 返回容器容量 --- capacity
  • 重新指定大小 --- resize
vector插入和删除

功能描述:

  • 对vector容器进行插入、删除操作

函数原型:

  • 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(); //删除容器中所有元素
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值