探秘C++之回炉重造
备注:本笔记作查漏补缺用,只记重点,只记干货,多的一句也不啰嗦!
文章目录
- 一、C++基础知识查漏补缺
- 1.1 C++数据类型
- 1.1.1 整型数据
- 1.1.2浮点型数据
- 1.1.3 字符型数据
- 1.1.4 字符串型数据
- 1.1.5 布尔类型
- 1.2 C++函数
- 1.2.1 函数的声明与定义
- 1.2.2 函数的分文件编写
- 1.3 C++指针
- 1.3.1 定义和使用指针
- 1.3.2 指针变量所占的内存空间
- 1.3.3 const修饰指针
- 二、C++核心编程
- 2.1 内存分区模型——内存四区
- 2.2 C++面向对象
- 2.2.1 构造函数
- 2.2.2 友元
- 2.2.3 运算符重载
- 2.2.4 多态
- 三、C++进阶部分
- 3.1 C++文件操作
- 3.1.1 文件类型及对应操作分类
- 3.1.2 文本文件的读写操作
- 3.1.3 二进制文件的读写操作
一、C++基础知识查漏补缺
1.1 C++数据类型
1.1.1 整型数据
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short(短整型) | 2字节 | -2^15 ~ 2^15 - 1 |
int(整型) | 4字节 | -2^31 ~ 2^31 - 1 |
long(长整型) | Windows为4字节,Linux为4字节(32位OS)或8字节(64位OS) | -2^31 ~ 2^31 - 1 |
long long(长长整型) | 8字节 | -2^63 ~ 2^63 - 1 |
备注:使用sizeof()可以获取某一变量或数据类型所占的字节数
1.1.2浮点型数据
数据类型 | 占用空间 | 有效数字范围 |
---|---|---|
float | 4字节 | 7位有效数字 |
double | 8字节 | 15~16位有效数字 |
备注:
- 默认情况下输出一个浮点数最多显示到小数点后5位
- 可以使用科学计数法表示一个浮点数,如0.003可表示为3e-3
1.1.3 字符型数据
C/C++中字符型变量只占用1个字节
字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入存储单元(在0~256范围内int和char可以相互转换)
1.1.4 字符串型数据
C风格字符串
char 变量名[] = "字符串内容";
C++风格字符串
string 变量名 = "字符串内容";
1.1.5 布尔类型
bool类型数据只有两个值:
- true —— 真(本质是1)
- false —— 假(本质是0)
bool类型占1个字节大小
1.2 C++函数
1.2.1 函数的声明与定义
函数的声明方式
type 函数名 (参数列表);
函数的定义方式
type 函数名 (参数列表){…函数体… return 返回值; }
备注:函数可被多次声明,但只能被定义一次
1.2.2 函数的分文件编写
作用:让代码结构更清晰
函数分文件编写一般有4个步骤:
- 创建后缀名为 .h 的头文件
- 创建后缀名为 .cpp 的源文件
- 在头文件中进行函数的声明
- 在源文件中进行函数的定义
1.3 C++指针
1.3.1 定义和使用指针
//指针定义的语法:数据类型 *指针变量名;
int a = 10;
int *p = &a;
cout << "*p = " << *p << '\n' << "p = " << p << endl;
//=> *p = 10
// p = 00AFF77C
备注:指针使用时前面不加 * 代表的是一个内存地址,加上 * 则代表的是这个内存地址中存储的数据。
1.3.2 指针变量所占的内存空间
指针变量在32位操作系统中所占的内存空间为4个字节,在64位操作系统中占8个字节。
1.3.3 const修饰指针
const修饰指针——常量指针
int a = 20; int b = 10; const int *p = &a; *p = 10;//错误,常量指针指向可以改,但指向的值不能改 p = b;//正确
const修饰变量——指针常量
int a = 20; int b = 10; int * const p = &a; *p = 10;//正确,指针常量指向不能改,但指向的值可以改 p = b;//错误
const既修饰指针又修饰变量
int a = 20; int b = 10; const int * const p = &a; *p = 10;//错误,const既修饰指针又修饰变量时指向不能改,指向的值也不能改 p = b;//错误
二、C++核心编程
2.1 内存分区模型——内存四区
C++程序在执行时,将内存大方向划分为4个区域:
代码区:存放函数体的二进制代码,由操作系统进行管理
存放CPU执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
全局区:存放全局变量和静态变量以及常量
全局变量和静态变量存在于此
全局区还包含了常量区,字符串常量和其他常量也存放在此
该区的数据在程序结束后由操作系统释放
栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
由编译器自动分配释放,存放函数的参数值,局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的数据在局部代码块执行完后由编译器自动释放。第一次使用局部变量地址时编译器会保留局部变量数值,第二次就被抹去了。
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统进行回收
由程序员分配释放,若程序员不释放,程序结束时由操作系统释放
在C++中主要使用 new 关键字在堆区开辟内存,利用 new 创建的数据,会返回该数据对应类型的指针
注意事项:堆区开辟的数据,由程序员手动开辟手动释放,释放时使用关键字 delete
内存四区的意义:
不同区域存放的数据,赋予不同的生命周期,使编程更灵活
备注:代码区、全局区在程序执行前构建,栈区和堆区在程序执行后构建
2.2 C++面向对象
C++面向对象的三大特性为:封装、继承和多态
C++认为万事万物都是对象,对象上由其属性和行为
2.2.1 构造函数
分类
- 按参数分:有参构造和无参构造
- 按类型分:普通构造和拷贝构造
调用方式
class Person{private:int age; public:Person(){}//无参构造Person(int a){//有参构造age = a;}Person(const Person &p){//拷贝构造age = p.age;} };
括号法
void test01(){Person p;//无参构造函数的括号法调用Person p2(10);//有参构造函数的括号法调用Person p3(p2);//拷贝构造函数的括号法调用,将p2身上的属性拷贝给p3 }
注意:调用无参构造函数时切记不要写括号,否则编译器会认为那是一个函数声明,而不是在创建对象
显示法
void test01(){Person p;//无参构造函数的显示法调用Person p2 = Person(10);//有参构造函数的显示法调用Person p3 = Person(p2);//拷贝构造函数的显示法调用 }
注意:
- 此方法可用于创建匿名对象,匿名对象特点:当前行执行结束后,系统会立即回收掉匿名对象
- 不要使用拷贝构造函数初始化匿名对象,编译器会报重定义错误,因为编译器会认为Person(p3) 等价于 Person p3
隐式转换法
void test01(){Person p2 = 10;//等价于Person p2 = Person(10);Person p3 = p2;//等价于Person p3 = Person(p2); }
拷贝构造函数的调用时机
C++中拷贝构造函数调用的时机通常有三种情况:
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给参数传值
- 以值方式返回局部对象
C++中构造函数的生成规则
默认情况下,C++编译器至少给一个类生成3个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数(对所有属性进行值拷贝)
生成规则:
- 如果用户定义有参构造函数,编译器不再生成默认无参构造,但会生成默认的拷贝构造
- 如果用户定义拷贝构造函数,那么编译器不会再生成其他构造函数
深拷贝与浅拷贝
浅拷贝:简单的赋值操作
深拷贝:在堆区重新申请空间,进行拷贝操作
注意:所有由编译器自动生成的拷贝构造函数,都是浅拷贝;想要深拷贝就必须自己写能实现深拷贝的拷贝构造函数,尤其是牵扯到指针变量的拷贝,都属于深拷贝
构造函数的偷懒写法——初始化列表
//Person类的构造函数 Person(int age, double height){this->age = age;this->height = height; }//初始化列表写法 Person(int age, double height): this->age(age), this->height(height) {}//注意:函数体虽为空,但不能省略
注意:初始化列表方式支持使用隐式转换法构造对象成员
2.2.2 友元
在程序里,有些私有属性也想让类外的一些特殊函数或者类访问,就需要用到友元技术。友元的目的就是让一个函数或者类访问另一个类中私有成员,关键字为friend
全局函数做友元
class House{//在类中声明友元函数friend void goodFriend(House &house); public:Building(){this->livingRoom = "客厅";this->bedRoom = "卧室";} private:string livingRoom;string bedRoom; }void goodFriend(House &house){//在House类的友元函数中查看House对象的所有成员cout << "全局函数goodFriend正在访问 " << house.livingRoom;cout << "全局函数goodFriend正在访问 " << house.bedRoom; }
友元类
class House; //House类的声明,避免编译器报错class GoodFriend{//定义goodFriend友元类 public:House *house;GoodFriend();void visit();//参观函数 }; GoodFriend::GoodFriend(){house = new House; } void GoodFriend::visit(){//访问House对象中的成员cout << "GoodFriend类正在访问 " << house->livingRoom << endl;cout << "GoodFriend类正在访问 " << house->bedRoom << endl; }class House(){friend class GoodFriend;//声明GoodFriend类是House类的友元类 public:string livingRoom;House(); private:string bedRoom; }; House::House(){this->livingRoom = "客厅";this->bedRoom = "卧室"; }
成员函数做友元
class House; class GoodFriend{public:House *house;GoodFriend(){house = new House;}void visit(){//友元函数cout << "visit函数正在访问 " << house->livingRoom << endl;cout << "visit函数正在访问 " << house->bedRoom << endl;}void nvisit(){//与友元成员函数区别的普通成员函数cout << "nvisit函数正在访问 " << house->livingRoom << endl;cout << "nvisit函数正在访问 " << house->bedRoom << endl;//报错,不是友元,无法访问House的私有成员} };class House{friend void GoodFriend::visit();//声明友元成员函数 public:string livingRoom;House(){livingRoom = "客厅";bedRoom = "卧室";} private:string bedRoom; };
2.2.3 运算符重载
运算符重载就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
Complex复数类,支持加减运算
#include<iostream> using namespace std;class Complex {private:double real;//实部double virt;//虚部 public:Complex();//无参构造函数Complex(double real, double virt);//含参构造函数Complex operator+(Complex &other);//加号重载Complex operator-(Complex& other);//减号重载void print();//输出函数 };//类中方法的实现 Complex::Complex(){real = 0;virt = 0; } Complex::Complex(double real, double virt) {this->real = real;this->virt = virt; } Complex Complex::operator+(Complex& other) {//复数加法的重载Complex temp(this->real + other.real, this->virt + other.virt);return temp; } Complex Complex::operator-(Complex& other) {//复数减法的重载Complex temp(this->real - other.real, this->virt - other.virt);return temp; } void Complex::print() {if (virt >= 0){cout << real << " + " << virt << "i" << endl;}else{cout << real << " - " << -virt << "i" << endl;} }
注意:
Java中的toString方法在C++中用重载左移运算符 << 来实现,且只能用全局函数重载左移运算符:(以复数类为例)
//传ostream&是为了返回cout对象,返回cout对象是为了实现链式输出 ostream& operator<<(ostream &cout, Complex &c){if (virt >= 0){cout << c.real << "+" << c.virt << "i" << endl;}else{cout << c.real << "-" << -c.virt << "i" << endl;}return cout; }//为了使全局函数能够访问到Complex类的私有成员,我们可以将这个全局函数设为Complex类的友元 class Complex{friend ostream& operator<<(ostream &cout, Complex &c);…… };
递增运算符的重载
class MyInteger {//声明友元函数friend ostream& operator<<(ostream& cout, MyInteger m); private:int data; public:MyInteger(int d) {data = d;}MyInteger& operator++() {//前置++运算符重载data++;return *this;//返回自身是为了能够实现连续操作}MyInteger operator++(int) {//后置++运算符重载//因为后置++运算的运算逻辑是先用后加,所以//1. 记录当时的结果MyInteger temp = *this;//2. 递增this->data++;//3. 返回递增前的结果return temp;//不返回自身的引用是为了防止当前对象被编译器当作局部变量在函数执行完后直接释放} };ostream& operator<<(ostream& cout, MyInteger m) {//<<运算符号重载cout << m.data;return cout; }
赋值运算符重载
编译器默认的赋值运算符是浅拷贝,类的属性中有用到深拷贝时就需要对赋值运算符进行重载了
class Person {friend ostream& operator<<(ostream& cout, Person& p); private:int* age;//创建一个堆区的属性 public:Person(int age) {this->age = new int(age);}~Person() {if (age != nullptr){delete(age);//释放堆区所占内存age = NULL;//防止非法访问,需要彻底断绝age与内存的联系}}Person& operator=(Person& p) {//先判断自身是否已经赋初值if (age == nullptr) {delete age;//若已经赋初值则释放掉}age = new int(*p.age);return *this;//返回自身是为了实现连续赋值} }; ostream& operator<<(ostream& cout, Person& p) {//重载<<运算符cout << *p.age << endl;return cout; }
关系运算符重载
//例:实现Complex复数类中 == 和 != 号的重载 bool operator==(Complex& other);//判断两个复数是否相等 bool operator!=(Complex& other);//判断两个复数是否不相等//具体实现 bool Complex::operator==(Complex& other)//判断两个复数是否相等 {if (real == other.real && virt == other.virt) {return true;}else {return false;} }bool Complex::operator!=(Complex& other)//判断两个复数是否不相等 {if (real == other.real && virt == other.virt) {return false;}else {return true;} }
仿函数——重载函数调用运算符()
仿函数在STL部分用处很大,因为该运算符实现后与真正的函数调用别无二致,因此得名仿函数。仿函数没有固定的写法,非常灵活
class MyPrint {//实例:打印输出类 public:void operator()(string text) {//重载函数调用运算符cout << text << endl;} };void test() {MyPrint m;m("hello, world!");//函数调用运算符的用法 }
2.2.4 多态
基本概念
多态是C++面向对象三大特性之一
多态分为两类:
- 静态多态:函数重载 和 运算符重载 属于静态多态,复用了函数名
- 动态多态:派生类 和 虚函数 实现运行时多态
静态多态和动态多态的区别:
- 静态多态的函数地址早绑定 —— 编译阶段 确定函数地址
- 动态多态的函数地址晚绑定 —— 运行阶段 确定函数地址
多态的实现
实现静态多态只需要重载函数或运算符即可,使其在代码编译时就能准确找到需要调用哪些方法或者运算符;而实现动态多态则需要使用到 virtual 关键字来构建虚函数和抽象类,通过在子类中重写对应的函数来实现动态多态
多态的优点
- 代码组织结构清晰,方便定位bug
- 可读性强
- 利于前期和后期的扩展和维护
纯虚函数和抽象类
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容。因此可以将虚函数改为纯虚函数
语法:virtual 返回值类型 函数名(参数列表) = 0;
当类中有了纯虚函数,这个类也就称为抽象类
抽象类特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到了堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或纯虚析构
虚析构和析构的共性:
- 可以实现父类指针释放子类对象
- 都需要有具体的函数实现
虚析构和纯虚析构的区别:
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构的语法:virtual ~类名(){}
纯虚析构的语法:
class 类名{virtual ~类名() = 0;
};
类名::~类名(){析构代码
}
注意:纯虚析构需要声明也要实现,所推荐使用虚析构
三、C++进阶部分
3.1 C++文件操作
程序运行时产生的数据都属于临时数据,程序一旦运行结束就会被释放
通过文件可以将数据持久化
C++中对文件操作需要包含头文件**<fstream>**
3.1.1 文件类型及对应操作分类
文件类型分为两种:
- 文本文件 —— 文件以文本的ASCII码形式存储在计算机中
- 二进制文件 —— 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
操作文件的三大类:
- ofstream:写操作
- ifstream:读操作
- fstream:读写操作
3.1.2 文本文件的读写操作
写文件操作
写文件操作的步骤:
首先要包含头文件
#include <fstream>
创建流对象
ofstream ofs;
使用流对象打开文件
ofs.open("文件路径", 打开方式);
文件的打开方式:
打开方式 解释 ios::in 为读文件而打开文件 ios::out 为写文件而打开文件 ios::ate 初始位置:文件尾部 ios::app 追加方式写文件 ios::trunc 如果文件存在,先删除,再创建 ios::binary 二进制方式写文件 注意:文件打开方式可以配合使用,需要借助
|
逻辑或运算符 ,例如以二进制方式写文件:ios::binary | ios::out
写数据
ofs << "写入的数据";
关闭文件
ofs.close();
总结:
- 文件操作必须包含头文件fstream
- 读文件可以用ofstream,或者fstream类
- 打开文件时候需要指定操作文件的路径,以及打开方式
- 利用<<运算符可以向文件中写数据
- 操作完毕要记得关闭文件
读文件操作
读文件与写文件步骤类似,但是读取方式相对较多
读文件步骤如下:
包含头文件
#include<fstream>
创建流对象
ifstream ifs;
打开文件并判断文件是否打开成功
ifs.open("文件路径", 打开方式);
读数据
四种方式读取:
//第一种读方式char buf[1024] = { 0 };while( ifs >> buf ) {cout << buf << endl;}//第二种读方式char buf[1024] = { 0 };while (ifs.getline(buf, sizeof(buf))) {cout << buf << endl;}//第三种读方式string buf;while (getline(ifs,buf)){cout << buf;}//第四种读方式char c;while ((c = ifs.get()) != EOF) {//EOF = End Of Filecout << c;}
关闭文件
ifs.close();
总结:
- 读文件可以利用ifstream,或者fstream类
- 利用is_open()函数判断文件是否打开成功
- close()函数关闭文件
3.1.3 二进制文件的读写操作
探秘C++之回炉重造相关推荐
- Vue回炉重造之封装防刷新考试倒计时组件
你好,我是Vam的金豆之路,可以叫我豆哥.2019年年度博客之星.技术领域博客专家.主要领域:前端开发.我的微信是 maomin9761,有什么疑问可以加我哦,自己创建了一个微信技术交流群,可以加我邀 ...
- 机器人学回炉重造(1-2):各种典型机械臂的正运动学建模(标准D-H法)
文章目录 写在前面 三连杆平面机械臂 平行四边形操作臂 闭链结构 例:平行四边形操作臂 球形臂 拟人臂 球腕 斯坦福机械臂 带球形手腕的拟人化机械臂 DLR机械臂 参考文献 写在前面 本文所有机械臂均 ...
- 《回炉重造 Java 基础》——集合(容器)
整体框架 绿色代表接口/抽象类:蓝色代表类. 主要由两大接口组成,一个是「Collection」接口,另一个是「Map」接口. 前言 以前刚开始学习「集合」的时候,由于没有好好预习,也没有学好基础知识 ...
- 机器人学回炉重造(5-2):关节空间规划方法——梯形加减速(与抛物线拟合的线性函数)、S型曲线规划
文章目录 写在前面 学习代码都记录在[个人github](https://github.com/xuuyann/RobotLearningCode)上,欢迎关注~ 梯形加减速(与抛物线拟合的线性函数) ...
- 真人电影中的幻想生物迷墙:索尼克为什么被骂到回炉重造?
<大侦探皮卡丘>上映在即,当网友们对着雷佳音配音的皮卡丘大呼好萌好萌时,我们仿佛又来到了大型真香现场--明明在几个月之前,当人们看到毛茸茸的大叔音皮卡丘时还每个细胞都充满了拒绝. 也有一种 ...
- 机器人学回炉重造(2-4):运动学奇异位型分析
文章目录 什么是运动学奇异位型? 例子:平面二连杆机械手的奇异位型 奇异位型解耦 腕部奇异位型 手臂奇异位型 转载:6轴串联关节机器人的奇异点 参考文献 什么是运动学奇异位型? 在初步系统地了解了机器 ...
- Vue回炉重造之封装一个实用的人脸识别组件
你好,我是Vam的金豆之路,可以叫我豆哥.2019年年度博客之星.技术领域博客专家.主要领域:前端开发.我的微信是 maomin9761,有什么疑问可以加我哦,自己创建了一个微信技术交流群,可以加我邀 ...
- 机器人学回炉重造(2-3):基本雅可比矩阵与其他雅可比矩阵
文章目录 基本雅可比矩阵 定义 求法 其他雅可比矩阵 定义 求法 补充:几何雅可比与解析雅可比 基本雅可比矩阵 定义 用笛卡尔坐标描述线速度(linear velocity)和角速度(angular ...
- 回炉重造之数据结构【一】基本概念
回炉重造之数据结构[一]绪论 文章目录 回炉重造之数据结构[一]绪论 数据结构的基本概念 基本概念和术语 数据结构的三要素 算法和算法评价 算法的基本概念 算法效率的度量 数据结构的基本概念 基本概念 ...
- javacript回炉重造之基础细节点
ascript之回炉重造 var n5=2e5 2*10的五次方 0x开头十六进制 0o开头八进制 0b开头二进制 typeof 用于检测数据类型 值类型(基本类型):字符串(String).数字(N ...
最新文章
- siblings获取不平级_siblings()是获取平级元素,哪不是平级应该怎么获取元素呢?...
- 李德毅院士:未来交通——自动驾驶与智能网联
- 利用Swoole同时更新多台服务器代码
- php安装openssl 扩展
- 在线建立或重做mysql主从复制架构方法(传统模式和GTID模式)【转】
- 《版式设计——日本平面设计师参考手册》—第1章应用对象样式
- python主成分分析实验报告_python进行主成分分析
- 选举学生会(洛谷P1271题题解,Java/C++语言描述)
- vue-ls vue 本地储存示例
- ES6 规格之相等运算符
- 与其纠结,不如放弃!
- 2000-2020年中国地面气象数据:从NOAA到分省面板
- 使用GDI绘制一个类似进度条的控件(1)
- windows 无法完成格式化_U盘无法格式化
- 逻辑函数表达式转化为标准形式
- 什么是jdk的java运行工具_下面( )是 JDK 中的 JAVA 运行工具。
- 中国数据中心IT基础设施第三方服务行业运行状况分析及未来发展趋势展望报告2022-2027年
- 假期作业二:主成分分析:步骤、应用及代码实现。代码可以用任何你熟悉的编程语言。
- 京东、宅急送的微服务实践分享(上)- 架构师小组交流会
- 链上自动化何以成就更好的 Web3 | 对话 OAK Network
热门文章
- QT界面怎么让控件跟随窗口大小变化
- stm32F1规避浮点运算
- CF949D Curfew(贪心)
- ffmpeg linux 升级_linux系统部署ffmpeg视频转码环境及使用方法 | linux系统运维
- php 递归的简单使用
- 放弃去FBI的机会,在美做了2年数据研究工作,如今回国创业,他把全球最顶级的智能预测算法带到了国内
- CSS / 清除浮动+切图+属性书写顺序+页面布局思路
- webgl中顶点着色器的varying变量,使用方法。
- 图像分类经典卷积神经网络—SENet论文翻译(中英文对照版)—Squeeze-and-Excitation Networks(挤压和激励网络)
- 论文阅读及代码学习-Directed Acyclic Graph Network for Conversational Emotion Recognition