黑马6笔记
创始人
2024-05-27 19:53:36
0

常量引用

函数中利用常量引用防止误操作修改实参

void showValue(const int& v) {
//v += 10;
cout << v << endl;
}

//发现是引用,转换为 const int* const ref = &a; 原本是值可以改 ,现在不能改了

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

函数默认参数

//1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
//2. 如果函数声明有默认值,函数实现的时候就不能有默认参数

3.3.2 函数重载注意事项

func(a); //调用无const  如果是变量传入,会调用可读可写的函数
func(10);//调用有const 常量传入,int &a= 10; 不合法   const in& a=10 合法 代码自动优化

封装

//三种权限
//公共权限 public 类内可以访问 类外可以访问
//保护权限 protected 类内可以访问 类外不可以访问
//私有权限 private 类内可以访问 类外不可以访问

struct和class区别

struct 默认权限为公共
class 默认权限为私有

对象的初始化和清理

构造函数和解析函数

括号法
person p1 默认构造函数调用
person p2(10) 有参构造函数
person p3(p2) 拷贝构造函数

调用默认构造不能 Person p2(); 如果加了编译器认为这是一个函数声明,不会认为在创建对象

//2.2 显式法
Person p2 = Person(10);
Person p3 = Person(p2);
等号右侧 Person(10)单独写就是匿名对象 当前行结束之后,马上执行析构函数,释放
//注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
person(p3) = = perspn p3

//2.3 隐式转换法
Person p4 = 10; // Person p4 = Person(10); 
Person p5 = p4; // Person p5 = Person(p4); 

//Person p5(p4); 拷贝构造函数

4.2.3 拷贝构造函数调用时机

构造函数调用规则

https://blog.csdn.net/MakeYouClimax/article/details/129088789

深拷贝与浅拷贝

属性初始化了一个指针 int* m_height;

堆区的数据手动开辟 手动释放
Person p1(18, 180);
Person p2(p1); 函数中执行了拷贝构造

m_height = new int(height);
int* m_height; 把身高的数据开辟到堆区 m_height = new int(height); 用指针接收堆区的数据
这个时候就需要析构代码,将开辟的数据做释放操作。
if (m_height != NULL)
{
delete m_height;
为了防止野指针的出现,可以再做置空的操作 m_height = null
}
此时报错了!

先进后出, p2 会先释放。但堆区内存已经被释放,p1再去会报错。
浅拷贝带来的重复释放报错
此时编译器提供的拷贝构造不太好,需要自己提供
m_height = p.m_height 需要更改为 m_height = new int(*p.m_height); 再开辟一块内存

初始化列表

类对象作为类成员

//构造的顺序是 :先调用对象成员的构造,再调用本类构造
//析构顺序与构造相反

先手机 再人
结束时候 先人 再手机

通过对象访问 p1.func();
通过类名访问 Person::func();

内存对齐

还是用一个例子带出这个问题,看下面的小程序,理论上,32位系统下,int占4byte,char占一个byte,那么将它们放到一个结构体中应该占4+1=5byte;但是实际上,通过运行程序得到的结果是8 byte,这就是内存对齐所导致的。

c++对象模型 和 this指针

成员变量和成员函数分开存储

空类 空对象占用内存空间为 1 个字节
是为了区分空对象占内存的位置
每个空对象有一个独一无二的内存地址

当你初始化了一个 int 变量 变为4字节
初始化了以恶搞static int m 还是4 因为静态变量不在类对象上
非静态成员函数 静态对象函数都不在类对象上

4.3.2 this指针概念

this指针指向被调用的成员函数所属的对象

解决名称冲突
返回对象本身

Person(int age)
{

this指针指向被调用成员函数所属对象 ,成员函数person,其所属对象为p1
//1、当形参和成员变量同名时,可用this指针来区分
this->age = age; # 防止age = age; 指针指向所属对象,所属对象指向age属性
}

p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1); 执行函数多次
函数调用完成后返回p2本身就能多次调用

return *this;  this指向对象指针  *号变成了对象本体
Person& PersonAddPerson(Person p) 返回引用  年龄增加到40  一直返回的对象本体
Person PersonAddPerson(Person p) 返回值 年龄是20 因为返回值 是拷贝生成一份新的 新的并不是对象本体

4.3.3 空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
p->ShowClassName(); //空指针,可以调用成员函数
但是如果成员函数中用到了this指针this->age = age;,就不可以了

4.3.4 const修饰成员函数

成员函数后加const后我们称为这个函数为常函数

常函数
class perspn
{public:
void showperson() const 加上 就可不可以修改m_a    const Type* const pointer 此时指针指向和指向值都不能改了
{							每个函数都有隐含的 this 指针
m_a =100; 此时可以				相当于this ->m_a =100;  /this指针的本质是一个指针常量,指针的指向不可修改
}int m_a;};
//person.mA = 100; //常对象不能修改成员变量的值,但是可以访问
person.m_B = 100; //但是常对象可以修改mutable修饰成员变量

页面信息错误 长对象不能调用普通成员函数,因为普通成员函数可以修改属性

友元

友元的目的就是让一个函数或者类 访问另一个类中私有成员

Building b;
goodGay(&b); 函数调用的是指针 所以要把地址传入

函数在类外实现

类做友元
goodGay::goodGay()
{
building = new Building; 由于上面申明的是指针 所以这里new 也是返回的
}
int* p = new int;
声明指针 p 指向内存

class house;
class gayer
{
public:
goodgay();
void visit();
private:
house *house;}
class house
{
friend class gayer;
public:
house();
public:
string keting;
private:
string woshi;
}
house::house()
{
this->keting="";
this->woshi="";
}
gayer::goodgay()
{}

继承

class A : public B;

A 类称为子类 或 派生类

B 类称为父类 或 基类

公共继承
类内可以访问 公共与保护权限
类外只能访问公共

保护继承
类内可以访问 全是保护权限
类外全都无法访问。

私有继承
类内可以访问 全是私有权限
类外全都无法访问
此时如果再来一个继承,即使是继承,类内全都无法访问

继承中的对象模型

父类中所有非静态成员属性都会被继承, 私有属性被编译器隐藏,但是继承了
此时 16个字节大小。
F: 跳转到F盘
cd 目录
dir
cl /d1 reportSingleClassLayout查看的类名 所属文件名

声明 子类,此时
//继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

继承同名成员处理方式

子类对象可以直接访问到子类中同名成员
子类对象加作用域可以访问到父类同名成员
当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

继承同名静态成员处理方式

通过对象访问
通过类名访问
::前面必须是类名 不是声明的类名称
s.Base::m_A
Son::Base::m_A

菱形继承

虚继承后数据只有一份

虚基类指针指向 虚基类表 ,表中记录数据偏移量。 分别+8 +4后就可以到达age

多态

多态是C++面向对象三大特性之一

多态分为两类

静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
动态多态: 派生类和虚函数实现运行时多态
静态多态和动态多态区别:

静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址

void DoSpeak(Animal & animal)
DoSpeak(cat);
父类的引用 但是传入了子类的对象
此处静态 。地址早绑定,在编译阶段就确定函数地址。
所以这里函数显示的是动物在说话

我们需要猫和狗说话 所以需要动态。地址晚绑定
此时,函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。子类virtual可写可不写

//多态满足条件:
//1、有继承关系
//2、子类重写父类中的虚函数
//父类指针或引用指向子类对象
Animal &animal =cat ;

重写和重载

重写是要 函数名 参数 全部相同

4.7.2 多态案例一-计算器类

如果想扩展新功能 需求修改源码
在真实开发中需要开闭原则
多态使用条件 父类指针或引用指向子类对象
int* p = new int;
​ 利用new创建的数据,会返回该数据对应的类型的指针
AbstractCalculator *abc = new AddCalculator;
virtual int getResult() 子类也重写了

纯虚函数和抽象类

virtual 返回值类型 函数名 (参数列表)= 0 ;

抽象类特点:
无法实例化对象
子类必须重写抽象类中的纯虚函数,否则也属于抽象类
void test01()
{
Base * base = NULL;
//base = new Base; // 错误,抽象类无法实例化对象 堆区开辟也不行
// Base b 不行 抽象类无法实例化对象
base = new Son; 子类必须重写抽象类中的纯虚函数,不然也不能创建对象
base->func();
delete base;//记得销毁
}

Base * base= new Son;
base->func();

虚析构和纯虚析构

m_Name = new string(name); new string指针返回 注意他这里是在子类里面
有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
虚析构和纯虚析构区别:

如果是纯虚析构,该类属于抽象类,无法实例化对象
if(m_Name != NULL)
{
delete m_Name;
m_Name = NULL;
}
后面
~Cat()
{
cout << “Cat析构函数调用!” << endl;
if (this->m_Name != NULL) {
delete m_Name;
m_Name = NULL;
}
}
在这里插入图片描述
没有执行猫的析构函数
父类的指针没能调用子类析构函数 咋曾内存泄漏
解决方式:将父类中的析构函数改为虚析构或者纯虚析构

析构函数 只要改父类的

纯虚析构 要改父类和子类
但是这里有点不一样 纯虚析构必须要有代码实现,光是有一句
virtual ~Animal() = 0; 不够
需要加上
Animal::~Animal()
{
cout << “Animal 纯虚析构函数调用!” << endl;
}

​ 1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
​ 2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
​ 3. 拥有纯虚析构函数的类也属于抽象类

多态案例三-电脑组装

在这里插入图片描述

文件操作

ofstream:写操作
ifstream: 读操作
fstream : 读写操作

cout 输出流对象

#include
#include
using namespace std;void test01()
{1 包含头文件2 创建流对象
ofstream ofs;
3 指定打开方式
ofs.open("text.txt",ios::out);   同级路径ofs << "姓名:张三" << endl;ofs << "性别:男" << endl;ofs << "年龄:18" << endl;ofs.close();}

读文件

if (!ifs.is_open()) 文件是否打开
while (ifs >> buf) 循环将三行数据全部读到后,循环会退出。读到头了会返回假的标志 \0

把文字读入 直到遇到假的标志 \0,然后执行while中语句

ifs.getline char* 这里数组名就是指向数组首地址,后面一个参数表明最多读取多少字符
所以 ifs.getline(buf,sizeof(buf))

getline(数据流,buf)第二个为准备好的字符串

while ((c = ifs.get()) != EOF) end of file 逐个读取字符 直到文件尾

二进制文件

二进制写

写字符串的时候不建议用string,会出现一些问题。建议用c语言的字符串char m_name[64];

char m_name[64];创建流对象 打开文件
ofstream ofs;
ofs.open()ofstream ofs("person.txt", ios::out | ios::binary);可以两部合成一部 ofstream ofs("person.txt", ios::out | ios::binary);ofs.write((const char *)&p, sizeof(p)); 
直接对p取地址,p是一个person型,但是取一个 const char* * 转换成相应的指针

后面可以sizeof(person)

二进制读

ifstream ifs("person.txt", ios::in | ios::binary);ifs.read((char *)&p, sizeof(p));

相关内容

热门资讯

安卓系统和oppo系统哪个流畅... 你有没有想过,手机系统哪个更流畅呢?安卓系统和OPPO系统,这两个名字听起来就让人心动。今天,咱们就...
安卓怎么用微软系统,利用微软系... 你是不是也和我一样,对安卓手机上的微软系统充满了好奇?想象那熟悉的Windows界面在你的安卓手机上...
安卓系统如何安装nfc,安卓系... 你有没有想过,用手机刷公交卡、支付账单,是不是比掏出钱包来得酷炫多了?这就得归功于NFC技术啦!今天...
ios系统可以转安卓,跨平台应... 你有没有想过,你的iPhone手机里的那些宝贝应用,能不能搬到安卓手机上继续使用呢?没错,今天就要来...
iOSapp移植到安卓系统,i... 你有没有想过,那些在iOS上让你爱不释手的app,是不是也能在安卓系统上大放异彩呢?今天,就让我带你...
现在安卓随便换系统,探索个性化... 你知道吗?现在安卓手机换系统简直就像换衣服一样简单!没错,就是那种随时随地、随心所欲的感觉。今天,就...
安卓系统安装按钮灰色,探究原因... 最近发现了一个让人头疼的小问题,那就是安卓手机的安装按钮突然变成了灰色,这可真是让人摸不着头脑。你知...
安卓7.1.1操作系统,系统特... 你知道吗?最近我在手机上发现了一个超级酷的新玩意儿——安卓7.1.1操作系统!这可不是什么小打小闹的...
安卓os系统怎么设置,并使用`... 你有没有发现,你的安卓手机有时候就像一个不听话的小孩子,有时候设置起来真是让人头疼呢?别急,今天就来...
安卓降低系统版本5.1,探索安... 你知道吗?最近安卓系统又来了一次大动作,竟然把系统版本给降到了5.1!这可真是让人有点摸不着头脑,不...
解放安卓系统被保护,解放安卓系... 你有没有想过,你的安卓手机其实可以更加自由地呼吸呢?是的,你没听错,我说的就是解放安卓系统被保护的束...
校务帮安卓系统下载,便捷校园生... 你有没有想过,你的手机里装了一个神奇的助手——校务帮安卓系统下载?没错,就是那个能让你轻松管理学校事...
安卓系统没有拼多多,拼多多崛起... 你知道吗?最近我在手机上发现了一个小小的秘密,那就是安卓系统里竟然没有拼多多这个应用!这可真是让我大...
甜城麻将安卓系统,解锁全新麻将... 你有没有听说过那个超级火的甜城麻将安卓系统?没错,就是那个让无数麻将爱好者为之疯狂的软件!今天,就让...
安卓系统卸载的软件,深度揭秘卸... 手机里的软件越来越多,是不是感觉内存不够用了?别急,今天就来教你怎么在安卓系统里卸载那些不再需要的软...
安卓系统推荐好游戏,畅享指尖乐... 手机里的游戏可是咱们休闲娱乐的好伙伴,尤其是安卓系统的用户,选择面那可是相当广呢!今天,就让我来给你...
王者安卓系统怎么卖,揭秘如何轻... 你有没有听说最近王者安卓系统的火爆程度?没错,就是那个让无数玩家沉迷其中的王者荣耀!今天,我就来给你...
安卓开发系统内置证书,基于安卓... 你有没有想过,你的安卓手机里那些神秘的内置证书,它们到底是个啥玩意儿?别急,今天就来给你揭秘这些隐藏...
荣耀安装安卓原生系统,深度体验... 你知道吗?最近荣耀手机界可是掀起了一股热潮,那就是——荣耀安装安卓原生系统!这可不是什么小打小闹,而...
安卓13小米系统,创新功能与流... 你知道吗?最近安卓13系统可谓是风头无两,各大手机厂商纷纷推出自家的新版系统,其中小米的安卓13系统...