嵌入式应用开发C++
作用:命名空间将全局作用域分成不同的部分不同的命名空间中的标识符可以同名不会产生冲突命名空间可以相互嵌套全局作用域也叫默认命名空间作用域限定符比命名空间优先级高命名空间使用:两种方法1.::2.using namespacec++里面使用c语言,头文件stdio.h或者cstdio。
基础

命名空间
作用:命名空间将全局作用域分成不同的部分
不同的命名空间中的标识符可以同名不会产生冲突
命名空间可以相互嵌套
全局作用域也叫默认命名空间

作用域限定符比命名空间优先级高
命名空间使用:两种方法1.::2.using namespace
c++里面使用c语言,头文件stdio.h或者cstdio
C语言和C++的不同
1.for循环
可以在for循环里面定义变量
2.寄存器变量
不能频繁添加,寄存器很小
C语言寄存器变量无法取地址,C++可以取寄存器变量地址,强行取地址,register无效
3.全局变量
C语言可以重复定义多个全局变量,同名的全局变量会被链接到全局数据区的同一块地址上
C++不允许重复定义全局变量
4.结构体

空的结构体,C语言的sizeof为0
C++里面sizeof为1
C语言结构体是一个集合(直接Test t1错误,需要添加struct),C++结构体是一种类型(可以直接写Test t1)
5.函数

函数不定义类型,C语言默认int,C++必须有返回值类型
bool类型

真是1,假是0,大小为1
条件运算符

引用
变量名
访问变量的两种方式:变量名,地址
引用:一个变量的别名
&两个含义,定义的时候,表示引用,前面没有类型,取地址

引用在定义的时候一定要初始化
int &k;
k = num; //错误

引用的大小取决于引用的对象
结构体,没有初始化,不正常的语法情况考虑到引用的本质:指针


不管什么类型,引用都在变量前面
void init(char *&s)
{
malloc(128); //此处操作指针s等价于操作main函数中的str
}
int main()
{
char *str;
init(str);
strcpy(str,"hello");
}
函数返回引用
1、跟返回局部变量的地址一样,不能返回局部变量的引用; 2、可以返回全局变量的引用; 3、使用引用来接函数的返回值。

常量指针和指针常量


函数
*****
1、C语言允许函数不写返回值;函数括号内不写参数表示能接受任意类型和个数的参数; 2、C++要求函数必须写上返回值;函数括号内不写参数表示不接受参数。
默认参数:一旦形参开始使用默认参数,后面都要使用默认参数
占位参数:不能用,但是调用的时候必须要传一个
int func(int a,int b,int);
int func(int a ,int b,int = 0); //占位参数和默认参数的结合
重载函数
判断标准:参数个数不同,参数类型不同,参数顺序不同
!!函数返回值不能作为重载函数的标准
匹配规则:1.精确匹配实参2.通过默认参数匹配实参3.通过默认类型转换匹配实参
匹配失败原因:1.有歧义
类和对象
面向对象:只有对象内部的代码能够操作对象内部的数据
对象只存储成员变量
特点:抽象(抓住事物的本质,而不是内部具体细节或具体实现)
封装(把对象的属性和操作结合在一起,构成一个独立的对象,限制属性和操作的权限)
多态
继承
优点:易维护,可读性高,现成的库,效率高,易扩展
缺点:编码效率高,运行效率低于C语言

类和结构体的区别:
类默认私有,私有只可在类里面用
结构体默认公有
类的实现方式:头文件(尽量写函数的声明但是不要去实现他)
面向对象案例:点和圆的关系
构造函数:
构造函数是特殊的成员函数,名字与类名相同,创建对象时由编译器自动调用,以保证每个数据都能有一个合适的初始值,并且在对象整个生命周期只调用一次。
默认,无参,有参,拷贝构造函数
调用无参函数不带括号
指向字符串常量,前面必须加const,所以第一个const必须加


可以不用初始化set
没有返回值
构造函数的特性
1.构造函数名与类名相同
2.构造函数为无返回值类型
3.对象实例化时编译器自动调用相对应的构造函数
4.构造函数支持函数重载
5.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。
析构函数destruct
内存泄漏,有malloc必须要free
没有返回值,没有参数


对象被释放时,自动调用析构函数
浅拷贝和深拷贝
浅拷贝:地址也拷贝一样的,不能释放两次内存
深拷贝:拷贝地址指向的内容,自己再申请一块内存。
凡是涉及地址,重写拷贝构造函数,用深拷贝、

对象初始化列表
类的成员变量加上const,只读变量

构造的顺序

对象初始化列表使用场景:
场景1:类的成员变量被const修饰


场景2:类对象作为另一个类的成员变量没有提供无参构造函数


场景3:基类没有提供无参构造函数

匿名对象:
手动调用,即创建匿名对象




构造中不要调用构造函数,容易产生匿名对象
new和delete
堆空间不会调用构造函数和析构函数,尽量不要用malloc和free


static静态成员变量
C语言中,修饰局部变量(延长声明周期),全局变量(限定作用域在本文件),函数(同全局)
静态成员变量,共享一块地址内存,一定要在类的外部初始化(分配空间)


静态成员函数
![]()
![]()
单例模式
运用static,静态成员函数和静态成员变量

面向对象模型
对象所占的大小,就是成员变量所占的大小,可以看作结构体
函数是共享的
const修饰的成员函数为常函数,只要函数不涉及修改函数,尽量加上const

友元函数和友元类


继承和派生
一个类从一个类获取成员变量和成员函数的过程
使用继承的两种典型场景:
1.创建新类与现有的相似,只是多出若干成员变量和成员函数
2.创建多个类,有很多相似 的成员变量和成员函数,提取公共成员
![]()
继承成员变量,需要把父类成员变量权限改为protected
继承权限决定继承的成员变量的权限
权限等级:public> protected>private
继承的权限不能超过继承权限,只能降级,不能升级
默认public最好,继承来的数据放在前面
创建派生类对象,一定会先调用基类构造函数
基类,派生,派生,基类
构造顺序:基类,成员,再自己

同名成员变量
基类和派生类有同名成员变量,默认隐藏继承来的,调用派生类的
若调用继承来的,用::作用域

派生类和基类一样,同样共享一个静态变量
多继承
构造顺序:按照继承的顺序来,哪个在前面,哪个先
多继承的问题:二义性(Base TestA TestB TestC)
A继承base,b继承base,c继承AB,此时C里面有两个base(浪费空间),无法区分c.base,需要加限定符明确A的还是B的
需要添加作用域限定符
解决方法,继承的时候添加virtual
:virtual public Person
虚基类指针,本身占8个字节
每个指针只能指向自己的部分
防止通过指针访问其他的成员变量
class TestA : virtual public Base

向上转型
可以把派生类赋值给基类
可以把派生类指针赋值给基类指针,基类指针赋值给派生类对象
可以把派生类引用赋值给基类引用
多态
同一条语句能表现出多种形态
三个条件:1.要有继承,2.虚函数重写3.要有基类指针指向派生类对象

p是parent * 类型,调用parent基类的show
但是创建的派生类对象 ,理应调用派生类,所以在基类和派生类里面加上virtual(派生类可加可不加),此时不属于静态联编


指向基类对象时,show调用基类
重载和重写的区别
运行期间根据具体对象类型决定调用的函数
多态原理


凡是有虚函数的类,里面都有虚函数表
不要把所有的函数声明成虚函数,调用效率低。


构造函数调用虚函数
构造顺序,先构造父类,再构造子类
构造函数无法实现多态
基类指针
父类指针指向子类数组,由于父类与子类p++步长不同,不要混搭
所以不要用基类指针指向派生类数组
虚析构函数
析构函数尽量写成虚析构函数,通过虚析构函数,可以释放派生类对象


动态类型识别
当不明确派生类类型时,可以用基类指针,指向派生类对象

到底能不能强转:p指向派生类可以转换,指向基类对象不能转换
多个枚举常量,中间用逗号隔开,最后一个不要加分号enum{}
三种方法:1.自定义类型2.动态类型强转dynamic_cast<强转类型>(强转对象)(必须多态才能使用)3.typeid
<1>

使用虚函数进行动态识别的缺陷:
1.必须从基类开始提供类型虚函数
2.所有派生类都必须重写类型虚函数
3.每个派生类的ID必须唯一
<2>

<3>
获得指向对象的类型
测试变量是声明类型的
有关键字type_info,要有头文件typeinfo


抽象类
实例:圆,矩形,三角形……
这一类事物的统称,不能写但必须要存在,例如求图形的面积(基类)
类似函数声名


如果派生类里面继承了抽象类,但没有重写虚函数,那个函数也会被认作纯虚函数,那个派生类也叫抽象类
C++中没有Java中的接口概念,抽象类可以代替接口问题
g++ *.cpp -o main -Wall
运算符重载
输出运算符<<只能重载成全局函数:左操作数不能修改
输入运算符>>istream
+可以全局或者成员函数(返回值是常量,常量不能给一般引用初始化)
=类可以直接赋值类
涉及到地址的,都要深拷贝
能作为左值的函数,都是引用= << >> [] +=
如何判断返回引用还是普通变量?
1.看该运算符能不能作为左值使用,如果可以,返回引用;
2.输入输出运算符支持链式编程,函数返回值充当左值,需要返回引用
3.无法修改左值时,使用全局函数进行重载
4.=,[],(),->只能通过成员函数重载
<< >> 前置++ []
相同类型的结构体不支持相加减
本质是函数的重载
运算符能重载成成员函数,也能被重载成全局函数
类型 类名 ::operator 符号(参数)
返回值 函数名(形参)


限制:不能重载的运算符 . :: .* ?: sizeof
原则:不改变优先级,不改变结合性,不改变操作数,不能创建新的运算符
输出运算符

赋值运算符
可以当左值,采用引用
重载成成员函数,涉及地址的拷贝,用深拷贝

自增运算符
后置++不能返回引用,后置++有个占位参数
前置++可以返回引用

下标运算符[]

函数调用运算符()
//类里面重载函数调用运算符,创建的对象称作函数对象(回调函数)

逻辑运算符
不能重载
本质就是函数的重载,重载了但是违背了短路原则,所以不要重载
mystring
string表示字符串
string name;
智能指针:4个
auto_ptr,shared_ptr,weak_ptr,unique_ptr 相当于模板类
自动释放所指向的对象(包含头文件memory)
uniue唯一,只能一个指向一个对象

模板
模板:两次编译
因此模板调用和编译在同一个文件里
函数模板
建立一个通用的函数,函数类型和形参类型不具体指定
其他都一样,只有类型不一样
template<typename T类属>或者<class T1,class T2>
隐式调用和显式调用

函数模板不会自动类型转换,普通函数可以自动类型转换(1,‘a')
注意:
1.函数模板可以被重载
2.C++优先考虑普通函数
3.如果函数模板可以产生更好的匹配,选择模板、
4.如果通过空模板实参列表的语法限定编译器只能通过模板匹配
show<>(1);
这样肯定调用模板
函数定义和调用必须在同一个文件里、
排序模板函数
搭配运算符重载
冒泡排序



类模板
类模板必须显式调用


模板继承
模板类派生普通类

模板类派生模板类

注意!派生类访问基类模板的成员变量,加上this
静态
涉及到模板类,一定要加<>

上面是4,下面是3
模板声明
全局函数,也可以写在里面

在类里面注意改成U
也可以直接在类里面写全局函数,但注意还是全局

hpp文件
:sp 文件名,打开另一个文件
ctrl ww切换文件光标到另一个文件
当模板的调用和定义分开来时,一个文件用hpp文件,这样方便整合到一起
强转
C语言强转存在的问题(过于粗暴,难于定位)
C++分成了四类
static_cast用于普通类型的转换,不能用于普通指针类型的转换,用于有继承关系的类指针之间的转换,用于有继承关系的类之间的转换
reinterpret_cast1.用于指针类型的转换,2.指针与普通的转换
dynamic_cast 类层次之间的转换
const_cast 用于去除const属性

引用会分配内存
5523地址相同,const保存在符号表里面,强行对常量取地址会强行分配内存,和引用的内存是同一块,地址取值里面都是3
异常
把函数调用 和函数处理分开来
throw
try
catch

错误通过异常会向上抛出
异常跨域函数
异常不会自动类型转换

异常类对象
抛出的对象,赋值给捕获的对象,通过拷贝的方式实现
对象 ,引用,指针
一般用引用来接,不会构造,少占用
推荐使用引用


异常案例

异常库
catch(exception &e)
{
cout << e.what() << endl;
}
文件
输入输出流
标准输入
cin是对象
文件存放在磁盘上,程序在内存上运行,读文件叫输入(从输入文件数据传给程序),写文件就是输出(从程序传给输出文件)


cin.getline()获取一行,包括空格
cin.ignore()忽略前面几个元素,然后再获取
cin.peek()从缓冲区读一个字节,但是保留在缓冲区
cin.putbach()把字符写入缓冲区

标准输出
输出要求指定格式,1.使用控制符的方法2.使用流对象的有关成员函数

成员函数


width宽度,字符长度不够,前面空着,后面写原来的
文件操作

STL概述
标准模板库
六大组件:算法algorithm,容易,迭代器iterator,仿函数function object,适配器adaptor,空间配置器allocator
通过迭代器可以实现容器和算法的无缝衔接
每用一个容器,包含一个头文件



容器
所有容器都是模板类
vector向量
是动态数组,可以随机存取元素[]或者at(),尾部添加移除元素很快,头部和中部插入移除费时
采用模板类实现

插入操作
insert


显示操作
1.迭代器,反应的是地址

2.【】或者at()

deque双端数组
数组名是首元素地址,不能改变,deque可以改变首元素地址
deque.push_back(elem); // 在容器尾部添加一个数据
deque.push_front(elem); // 在容器头部插入一个数据
deque.pop_back(); // 删除容器最后一个数据
deque.pop_front(); // 删除容器第一个数据 (是引用,可作为左值)
deque的大小

删除(迭代器)

死循环了,因为deque删除之后数组哪边少,移动那边,最后会移动end,再执行++,it指向it后面了,所以利用返回值
栈stack和队列(没有迭代器)
不支持遍历
出栈不反悔元素,要查看元素只能用top
list双向链表容器
不可以随机存取元素,不支持at()函数与[],it++可以,it+5不可以
是相当于指针->
priority_queue优先队列(没有迭代器)
是个模板类,默认用的vector也可以deque,不是用的队列
![]()
<>里面都是类型,less是模板类,重载了()
less或者greater<>
数据放入容器中,不需要指定位置,会自动排序
和队列差不多,头文件是queue
set集合容器
树形结构
set插入数据比较多,且需要排序的时候用
不能指定插入位置,不可以直接存取元素at.()和[]
multiset和set区别:前者可以元素重复,后者不可以,只能出现一次
插入新元素必须删除之前的
![]()
![]()
迭代器,对于复杂的类型,可以直接写auto
erase()一个元素,一个区间
insert()
查找按照排序的重载,提高了查找的效率,进去按照学号排序,查找按照学号查找
find返回一个迭代器



两个迭代器封装在pair里面

map
可以通过迭代器遍历
it是个指针
插入用insert和map[]
区别:insert插入重复会报错,map[]会覆盖

删除
位置,元素,区间

总结

有对应关系,选择map,只是单一数据排序,set
算法
头文件algorithm,numeric,functional组成
算法里带if,是指条件
谓词
for_each不可以修改,transform可以修改数据
查找
find返回元素迭代器


绑定了第二个参数变成4

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)