导图社区 C++
这是C++初学者的一些随笔。汇总了C++初学者在学习过程中的重要概念、基础语法、常见错误及学习技巧,为C++新手提供一条清晰的学习路径。
编辑于2024-08-30 20:06:38C++,贝尔实验室
基本语法(兼容C语言),C++新增语法和修改C的部分(C:底层操作系统,音视频开发,C++:大型项目,嵌入式设备开发,应用层和算法开发)
面向对象OOP
对象+对象之间的通信方式=程序 对象=数据结构+算法
多个子程序和对象组成,特点:重用,灵活,可扩展
封装
代码复用
模块化
安全
继承
复用
扩展
多态
函数重载和泛型编程
静态多态
虚函数重写
动态多态
泛型编程
宏定义不做类型检查
基础语法
常量
#define SIZE 1024
在C语言中不会检查类型
C++用const创建常量
const可以用指针间接修改C语言中
const unsigned int SIZE=1024
C++里面不能被修改
头文件iostream
名词空间
namespace xxx { 变量 函数 类 } using namespace xxx:全局访问,输入输出,std,string 否则要用xxx::(访问符)才能访问
作用:区分不同的同名变量,或者函数
引用
给变量改别名,函数传参和返回值
int &(引用类型)
int a; int & b=a; a这片空间有2个名称
传参
形参可以改变实参的值,不会有额外开销
返回
注意不要返回一个局部的引用,返回的是一个变量本身,可以直接对其操作
只适用于静态变量和全局变量这些出了作用域不会被销毁的对象
特性
引用在定义时必须初始化上面的b=a这种
输入输出
cin:标准输入>>变量名 cout:标准输出<<变量名<<endl endl换行 cout.width(空间数) cout.seft(ios::向哪对齐)
oct:8 dec:10 hex:16
空
C语言:NULL=unsigned long,可以赋值给一个非指针,不严谨,不做类型检查
C++:空指针,只能赋值给指针类型
nullptr
函数重载
C语言使用不定参数
C++:使用函数重载
当函数的逻辑相似时,使用函数重载
逻辑一样时,使用模板
函数名相同,参数或者返回值不同(相似)
函数可以有默认参数
默认参数的顺序从右往左,
调用时传参从左往右
注意函数重载和默认参数可能会造成二义性
子主题
堆区空间
C语言:malloc和free都是函数
C++:
new:是运算符,使用异常bad_alloc而不是返回值
单个变量 new 类型
int *p=new int; int *q=new int [4]
数组:new 类型[数字]
delete:也是运算符
单个变量 delete 变量名
数组 delete [] 变量名
内联函数
不能回调
直接展开在调用的位置,执行效率高,执行频率高
要求
代码逻辑不能复杂,不能有while类似的逻辑
一般不能超过5行
inline
inline 返回值 函数名(参数)
结构体=类
c语言中的结构体不能写函数
C++:可以写函数
类(面向对象的都有)class
一类事物的抽象
属性:成员变量
行为:成员函数
类的示例化叫做对象
小侯
小刘
class struct { 属性 行为 }
当成员函数的参数名和属性同名时
在成员函数里面使用这个参数时前面加this指针
权限
public:共有的
private:私有的
protected:保护的
类被创建出来之后(会自动生成5个函数)
构造:普通构造,拷贝构造,创建对象自动调用
不能指定返回类型
有唯一形参
和类同名
子主题
功能:初始化数据成员
不是普通成员函数,是特殊的调用时,不能直接:类名 对象名;对象名 构造函数名 只能 类名 对象名(参数)(没有参数时不要括号和参数)
移动构造
临时变量:赋值之后自动释放
功能:用于将资源从一个临时对象转移到新创建的对象中&&
classname obj1 classname obj2=std::move(obj1)
要显示 ./a.out -fno-elide-constructors
默认构造函数
组成
无参构造函数
全缺省构造函数
自动生成构造函数
无参和全缺省同时存在会造成二义性
无参构造函数
调用时不要加括号,会被认为是函数声明
拷贝构造
分类
浅拷贝
在不涉及地址时使用
把类的属性数据拷贝一遍
深拷贝
在动态分配资源时使用
和类同名
类名(const 类名 & 参数名)
这个参数名是原来对象的别名,&表示对原来对象的引用
mystring str2(str1),把str1的拷贝给str2或者 mystring str2=str1或者
使用一个对象str1初始化str2对象
功能:创建一个新对象,是现有对象的副本
赋值构造
功能:将一个已经存在的对象的内容赋值给另一个已经存在的对象
classname obj1 classname obj2 obj1=obj2
析构:销毁对象自动调用
=
&
static在类中的应用
在C语言
修饰全局变量,限制作用为本文件
修饰局部变量,延长生命周期
修饰函数,限制作用域在本文件
在类里面
修饰的成员变量:不计入类的大小,不属于类
需要初始化
类型名 类名::变量名=数值
非static修饰的函数可以访问static修饰的变量和函数
static修饰的函数可以访问static修饰的变量
static修饰的函数不能访问非static修饰的变量
static修饰的函数不能访问非static修饰的函数
变量
在内存中只有一份
由所有类对象共享
需要在类外初始化,
函数
不依赖于特定对象
没有this指针
只能访问静态成员
应用
可以记录某个类创建示例(对象)的个数
单例模式
只允许产生一个对象
singleton
拷贝构造,=禁用
普通构造作为非public,限制构造
有一个共有的静态函数去创建一个静态的对象并且返回
创建实例时需要使用引用去获取到对象的控制权
工厂模式
观察者模式
适配器模式
const在类中的应用
修饰变量
初始化:只能用构造函数初始化列表
:变量名(参数)
同名不需要用this
非const修饰的函数可以访问const修饰的变量
const修饰的函数可以访问const修饰的变量
const修饰的函数可以访问非const修饰的变量
const修饰的函数不可以访问非const修饰的函数
const修饰的函数内部不能出现修改的逻辑,不能修改成员变量,不能访问成员变量的引用
修饰函数
返回不能是成员变量的引用
友元函数
功能:让一个函数或者类访问另一个类中的私有成员,打破封装
不需要繁琐的安全验证,效率更高
友元函数是单向的
应用
需要频繁访问私有成员的逻辑会使用友元函数
做某些运算符重载时,必须用友元函数
子主题
全局函数作为某一个类的友元函数
friend void func(A&obj)
一个类作为另外一个类的友元类
一个类的成员函数作为另一个类的友元函数
friend在谁的类里面,就可以访问谁的私有函数
运算符重载
作用
让类这种类型可以支持运算符操作
希望对象之间也能用这些运算符进行运算
特点
不能被重载的运算符
逗号
三目运算符
sizeof
作用域运算符::
指针运算符.*
只能重载已经有的运算符
不能改变运算符原来的特性
运算符重载不能有默认参数
格式
返回值类型 operator 运算符 (形参表)
成员函数重载
参数会比运算符的目数少一,因为有一个参数是本身
this默认第一个参数
全局友元函数重载
参数的数目和运算符的数目一致,并且可以按照要求规定参数的顺序
最常见的就是使用友元函数重载输入输出重定向
子主题
模板
功能:让程序员能写出与类型无关的代码
泛型编程
子主题
全局函数模板
函数模板
可以隐式调用,让编译器根据实参推演模板参数的实际类型
add()
也可以显式调用,在函数名后<>中指定模板参数的实际类型
add<强制转化类型>()
不是函数而是模板,通过模板的实例化生成函数的
是生成函数的
模板调用
模板名<实际类型参数,实际类型参数1>
template<class T,class T1,class 参数类型........>(这里装的是模板参数T根据实参来觉得自己是int ,char 还是double等) T(返回值类型) add(模板名)(T a, T b(形参表)) { 函数体 }
函数模板可以有非模板参数,并且这个参数可以有默认值
T(返回值类型) add(模板名)(T a, T b(形参表),int c=10)
函数模板模板参数,可以有默认值,但是不建议使用
template<class T,class T1=int,class 参数类型........>(这里装的是模板参数)
函数模板模板参数,可以有非类型参数模板,并且可以有默认值,但是这个默认值只能读不能改
template<class T,class T1,class 参数类型........int a=3>(这里装的是模板参数)
类模板
子主题
模板类在创建对象时必须调用显示
类模板名<实际类型名> 对象名(实参列表)
类模板需要使用这个类的类型时,必须指定模板参数
类模板,的友元函数必须是一个函数模板
在类模板外定义成员函数时,应写成类模板形式
template <class 虚拟类型参数> 函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参列表)
生成类
类模板实例化需要在类模板名字后面跟<>,然后将实例化的类型放在<>中
template <class T,class T1.....> class 类模板名{ }
template <class T> class A { }
A是类名,A<T>才是类型
模板不能分成.h和.c
它的声明和实现要在一起.hpp
模板的定义和声明可以分离
分开时,定义和声明都需要给模板参数
继承
为了扩展,在一个已经存在的类的基础上,新建一个类
基类
派生类
子类
父类
代码重用,新增代码,替换已有代码
除了构造和析构以外的成员全部吸收进入派生类,虽然不继承,但是会调用基类的构造和析构
class A { } class B :public A
构造和析构顺序
构造:先构造基类,再构造派生类 析构:先派生,再基类
栈
继承时基类构造传参问题
初始化列表传参
通过派生类的构造函数的初始化列表传递
继承方式
private
所有变成私有
public
所有的权限不变
protected
共有变成保护,其余不变
需要被外界访问的成员直接设置为public 只能在类内访问的成员设置为protected
特点
被继承到派生类的私有成员,使用基类的共有接口和保护接口进行访问
被继承到派生类的保护成员,可以通过派生类的所有接口访问,也可以通过基类的共有接口或保护接口访问
类外不能访问
权限说明
public所有位置都能用
protected:类的内部,派生可用
private:类的内部
多继承和多重继承
多重继承:class A class B :public A class C:public B
多继承:class A class B class C :public A,public B
先构建基类是先继承
多继承和多重继承同时使用会造成二义性
派生继承2个基类,但是这两个基类是同一个基类的派生类,会有二义性
解决,用虚继承
class A class B:virtual public Base
创建派生类对象
派生类里面创建一个与基类无关的类
执行顺序
先基类构造函数,然后成员对象构造函数,最后派生构造函数体
多态
同一接口不同实现,运行时确认(动态联编)
多态的目的:通过基类指针对所有派生类(包括直接派送和间接派生)的成员变量和成员函数进行全方位的访问,尤其是成员函数,如果没有多态,我们只能访问派生的成员变量
虚函数
virtual +函数(类似于static)
虚函数表
不会继承
只要基类有virtual,派生类加不加都一样
什么叫多态
定义:同一接口的不同实现
特点:动态联编在运行时确认函数
实现方式:使用虚函数,在继承时实现覆盖重写
:因为有虚函数存在,对象空间8字节会存储虚函数表的首地址,可以通 过地址找到虚表,虚表中存储的是不同对象自己的虚函数,实现动态联编
行为不占大小
函数也不占大小
重写和覆盖
不同作用域,名字相同,必须有virtual,不能有static
派生类和基类同名的函数用virtual实现多态
重载
作用域相同,名字相同,参数不同
同一个类里面,函数名相同,但是参数不同
和默认带参会产生二义性
隐藏
作用域不同,名字相同,返回参数不同
派生类中同名同参方法代替基类的现象(和基类)
也叫重定义
析构最好写虚函数,构造不需要
派生类不能继承基类的构造函数
析构,如果没有使用虚函数,谁的指针就用谁的析构,
纯虚函数
virtual void func()=0
virtual 返回值类型 函数名 (函数参数)=0
没有函数体,只有函数声明
不占空间
拥有纯虚函数的类叫做抽象类,不能创建实例(对象)
限制构造和纯虚函数的区别
限制构造
构造函数不是共有的,继承之后可以通过派生类间接创建对象
构造函数的权限不是public,就是限制构造函数
基本限制构造函数不能创建实例,只能派生出子类,定义子类对象来访问接口函数
构造函数不能是纯虚函数
抽象类的所有方法必须重写
抽象类只能作为基类来派生新类
什么时候声明虚函数
看成员函数所在类是否为基类,然后看成员函数的类在继承后有无修改的问题,如果上面都符合就要写加virtual
为什么用基类的指针指向派生类地址
当需要一个指针指向不同类型空间时,就可以让这些空间继承同一个基类,这样基类的类型指针就可以指向不同的空间,实现多态
通过指针访问类的成员函数
如果该函数不是虚函数,编译器会根据指针类型找到该函数=指针是哪个类的类型就调用哪个类的函数
如果该函数是虚函数,编译器会根据指针的指向找到该函数=指针指向的对象属于是哪个类的类型就调用哪个类的函数
工厂设计模式
简单工厂,工厂方法,抽象工厂
纯虚函数应用
观察者模式
纯虚函数应用
string
类,在string头文件中,用于表示和操作字符串,比char好用
提供:length()和size()成员函数来获取字符串长度
可以用(==,!=,<,>,<=,>=)比较字符串大小
内存泄露
内存空间得不到释放,除非程序运行结束有操作系统回收,
转换函数
隐式转换
不安全
构造
其他类型转换为类的类型
自定义转换函数
把类的类型转换为其他类型
少用不安全
容易和运算符重载造成奇义
operator放在函数前面
这两个都是应用
强制转化
安全
int(a)基本数据类型
C++形势
(类型)变量
C形势
标准转换函数
reinterpret_cast
指针类型转换
指针类型转换为整型
uintptr
intptr
是个模板
const_cast
const指针和普通指针之间的转换,一般不要const转非const---不安全
强转有的行为它有,但是它的行为强转没有
static_cast
基本数据类型转换
具有继承类型转换
没有虚函数时用
dynamic_cast
继承类对象间转换,类中含有虚函数才能用
基类指针指向派生类,派生类指针不要指向基类
类型检测功能,比static_cast安全
运行时类型检测
explicit
防止隐式转换(调用)
放在函数前面
有这个后面2种语句无法通过构造函数
类型名 变量=输入参数
类型名 变量={输入参数......}
如下语句调用拷贝构造
类型名 变量=另一个已经存在的同类型变量
class MyClass {} MyClass a; MyClass b=a;
函数参数的对象传递
MyClass b; void fun(MyClass a); func(b);
作为参数的返回值
MyClass fun(){ MyClass b; return b; }
A a=func()//编译器会优化不会有临时变量的产生
STL复用提升
标准模板库,容器的集合,惠普实验室
容器包含
序列式容器
vector
顺序表
动态开辟,单项插入操作(尾插)
iterator是vector遍历函数
v.push_back():放入数据
v.insert(指针,数据):插入数据
v.erase():删除
v.assign(num,val):放入num个val
v.assign(itstart,itend)
支持随机存取
尾插快
索引存取
list
双向链表
不提供随机存取
不能索引
在哪插入和删除都快
push_back
push_front
insert
pop_back
pop_front
erase
remove
clear
find
deque
双向队列
索引直接存取
rerise重新设置空间的大小
数组
在头部和尾部添加和删都快
也可随机存储
关联式容器
set
红黑树
二叉树
不能有重复元素,自动排序
主要做查询
map
红黑树,键值对
不能有重复元素,键值自动排序
string类在头文件
功能
查找find
拷贝copy
删除delete
替换replace
插入insert
不要担心内存释放和越界的问题
操作
string构造函数
string()
赋值操作
子主题
标准化组件
组成
容器
存放数据结构,用来存放数据
迭代器
指针
容器和算法之间的胶合剂
空间配置器
分配动态空间
配接器
修饰容器,仿函数,迭代器接口
算法
增删改查,存放算法
质变算法:运算过程中会更改区间内的元素
copy
替换
删除
非质变算法:运算过程中不会改变区间内的元素
find
返回迭代器
for_each
sort
sort(container.begin(),container.end())元素升序排序
计数
遍历
仿函数
STL的优点
是C++的一部分,不需要额外安装,高可重用性,高移植性和跨平台的有点
将数据和操作分离,数据由容器加以管理,操作则由算法定义,
6大组件的关系
容器通过空间配置器获取数据空间,算法通过迭代器存储容器中的内容, 仿函数可以协助算法完成不同的策略变化,适配器修饰函数
异常
异常
库的创建者知道错误何时发生,但是不知道怎么处理
用户不知道错误何时发生,但是知道发生错误时,该怎么处理
处理异常
异常处理3步机制
检查(用户)---抛出(库作者)---捕获(用户)
抛出
throw 异常类型(“错误提示”)
构造函数和析构不能抛出异常
构造函数抛出异常时,析构函数将不会被执行,需要手动去释放空间
析构也不能抛出异常
检查
try{ 被检测的语句//保护代码 }
捕获
catch(异常类型&err) { cout<<err.what()<<endl打印错误信息//块 }
catch(...),接受一切throw信号
如果有多个catch,catch(...)必须放在最后
标准异常
undenrflow_error
发生数字下溢,会抛出该错误
如果需要更详细和继承的异常:需要使用stdexcept
如果只是使用异常:可以使用exceptiond头文件
overflow_error
发生数字上溢,会抛出该错误
bad_alloc
异常处理方式
自定义异常
与operator有二义性
使用标准异常
继承标准异常
智能指针RALL
功能
管理对象空间,防止内存泄露,释放无名对象的空间(new.....)
在智能指针中,一个对象什么情况下被析构或被删除有指针本身决定,不需要用户手动管理
当有多个程序想访问一个数据内容但又没有这个数据内容的权限,就可以使用
组成,本质都是模板类
shared_ptr
共享智能指针,可以多个共享指针,指向同一片对象空间
原理:使用引用计数实现对同一块内存的多个引用,在最后一个引用被释放时,指向的内存才释放
当没有共享指针指向这片空间时,会自动释放这片空间
static
写法
shared_ptr< 类的类型> p=make_shared<类的类型>(类的构造函数())
unique_ptr
对象封装一个原始指针,并负责其生命周期,当该对象被销毁时,会在其析构函数中删除关联的原始指针
不支持普通拷贝和赋值操作,但是可以将所有权进行移交
move
unique_ptr<类的类型> p(new 构造函数())
weak_ptr
弱指针,管理共享指针,不能直接操作空间,需要转换为共享指针才能操作空间(lock,共享指针的构造函数来转换)
防止共享指针循环引用
函数
expired:观测资源是否已经被释放
lock:获取管理所监测资源的shared对象
reset:清空对象
本质:智能指针不是指针,是一个管理指针的类,