黑马程序员C++学习笔记(第二阶段核心:面向对象)(二)
目录
- C++对象模型和this指针
- 成员变量和成员函数分开存储
- this 指针
- 空指针访问成员函数
- const 修饰成员函数(只读)
- 友元 -- friend (类外写成员函数)
- ☆☆运算符重载
- 加号运算符重载
- 左移运算符(<<)重载
- 递增运算符重载
- 赋值运算符重载(存在坑:堆区内存重复释放--深浅拷贝)
- 关系运算符重载
- 函数调用运算符重载(仿函数)---匿名函数对(classs())
C++对象模型和this指针
成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上面(类对应的对象的内存大小不会发生改变)
空对象占用内存空间为:C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置每个空对象也应该有一个独一无二的内存地址
this 指针
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码。那么问题是:这一块代码是如何区分是哪个对象调用自己的呢?
c++通过提供特殊的对象指针,this指针,解决上述问题。
this指针指向被调用的成员函数所属的对象
this指针是隐含在每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途:
- 当形参和成员变量同名时,可用this指针来区分;(解决名称冲突)
- 在类的非静态成员函数中返回对象本身,可使用return *this
#include<iostream>
using namespace std;class Person {public:Person(int age) {// age=age; //程序将这两个看成一个整体了// 使用this指定age;// 解决名称冲突this->age = age;}// 返回的是引用而不是值Person& personAddage(Person &p) {this->age += p.age;return *this;}int age;
};int main() {Person p(10);cout << p.age << endl;Person p2(10);// p2.personAddage(p);// cout<<p2.age<<endl;// 链式编程思想p2.personAddage(p).personAddage(p).personAddage(p);cout << p2.age << endl;return 0;
}
空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
判断this指针是否为空来防止出错
if(this==NULL){return ;}
# include<iostream>
using namespace std;class Person{public:void showClassname(){cout<<"this is Person class"<<endl;}void showPersonAge(){// 报错指针是因为传入的指针为NULL,没有指明对象// 下面默认代码为:cout<<"age= "<<this->m_age<<endl; // 使用this指明对象属性// 补充代码if(this==NULL){return ;}cout<<"age= "<<m_age<<endl;}int m_age;
};int main(){Person *p=NULL;p->showClassname();p->showPersonAge();return 0;
}
const 修饰成员函数(只读)
常函数:mutable
- 成员函数后加const后我们称为这个函数为常函数
- 常函数中不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数和常对象下依然可以修改
class Person{public:// this 指针的本质是 指针常量 , 其指向是不可以被修改的;// Person * const this// 在成员函数后面加const 修饰的是this 指针,使其指向的值也不可以修改void showPerson()const{ // const Person *const this --使指向的值也不可以被改变// m_A=100;m_B=20;}int m_A;mutable int m_B; //特殊变量,即使在常函数中也可以修改这个值,加关键字 mutable
};
常对象:(常对象不可以调用不同成员函数,因为普通成员函数可以修改属性,而常对象是不可以修改成员属性的)
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数,不允许修改普通的成员属性
友元 – friend (类外写成员函数)
在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术。友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的关键字为 friend
友元的三种实现
- 全局函数做友元(函数参数为类指针)
- 类做友元
- 成员函数做友元
1. 全局函数做友元
函数参数为类指针;函数参数为类引用;
#include<iostream>
#include<string>
using namespace std;class Building{//可以访问到Building类中私有内容friend void goodGay(Building *Building);friend void goodGay1(Building &Building);public:Building(){m_SittingRoom="客厅";m_BedRoom="卧室";}string m_SittingRoom;private:string m_BedRoom;
};// 全局函数
void goodGay(Building *Building){cout<<Building->m_SittingRoom<<endl;// 在类外可以访问私有属性cout<<Building->m_BedRoom<<endl;
}
void goodGay1(Building &Building){cout<<Building.m_SittingRoom<<endl;// 在类外可以访问私有属性cout<<Building.m_BedRoom<<endl;
}int main(){Building p;goodGay(&p);goodGay1(&p);return 0;
}
2. 类做友元
#include<iostream>
#include<string>
using namespace std;// 提前声明,下面防止报错 Building * building;
class Building;
class GoodGay{public:GoodGay();Building * building;// 访问 Building 中的属性void visit();
};class Building{// 定义 友元friend class GoodGay;public:Building();string m_SittingRoom;private:string m_BedRoom;
};// 类外写成员函数
// 构造函数
Building::Building(){m_SittingRoom="客厅";m_BedRoom="卧室";
}
GoodGay::GoodGay(){building=new Building;
}
// 类外写成员函数
void GoodGay::visit(){cout<< building->m_SittingRoom<<endl;cout<< building->m_BedRoom<<endl;
}int main(){Building p;GoodGay gay;gay.visit();return 0;
}
3. 成员函数做友元
#include<iostream>
#include<string>
using namespace std;class Building;
class GoodGay{public:GoodGay();void visit();//使其可以访问私有属性void visit2();Building * building;
};class Building{// 设置 GoodGay 作用域下的友元函数,使其可以访问私有属性friend void GoodGay::visit();public:Building();public:string m_sittingRoom;private:string m_badRoom;
};// 类外实现成员函数
Building::Building(){m_sittingRoom="客厅";m_badRoom="卧室";
}
GoodGay::GoodGay(){building =new Building;}void GoodGay::visit(){cout<<building->m_sittingRoom<<endl;cout<<building->m_badRoom<<endl;
}
void GoodGay::visit2(){cout<<building->m_sittingRoom<<endl;//cout<<building->m_badRoom<<endl;
}int main(){GoodGay gg;gg.visit();gg.visit2();return 0;
}
☆☆运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
对于内置数据,编译器知道如何进行运算;
对于个人定义的数据类型而言,需要根据需求对运算符进行重装载(如:两个类对应属性相加)
加号运算符重载
#include<iostream>
using namespace std;class Person{friend void print(const Person &p);friend Person operator+(const Person& p1,const Person& p2);private :int m_a;int m_b;public:Person(int a=0,int b=0){m_a=a;m_b=b;}
};
// 全局函数
Person operator+(const Person& p1,const Person& p2){Person temp;temp.m_a=p1.m_a+p2.m_a;temp.m_b=p1.m_b+p2.m_b;return temp;
}void print(const Person& p){ //定义常量引用,防止误操作cout<<p.m_a<<p.m_b<<endl;
}
int main(){Person p1(1,2),p2(3,4);Person p3;// 通过全局函数重装载p3=operator+(p1,p2);// 简化为p3=p1+p2;print(p3);return 0;
}
#include<iostream>
using namespace std;class Person{friend void print(const Person& p);friend Person operator+(const Person& p1,const Person& p2);private :int m_a;int m_b;public:Person(int a=0,int b=0){m_a=a;m_b=b;}Person operator+(Person& p);
};
// 类外定义成员函数
Person Person::operator+(Person& p){Person temp;temp.m_a=p.m_a+this->m_a;temp.m_b=p.m_b+this->m_b;return temp;
}void print(const Person& p){ //定义常量引用,防止误操作cout<<p.m_a<<p.m_b<<endl;
}
int main(){Person p1(1,2),p2(3,4);Person p3;// 通过成员函数重装载p3=p1.operator+(p2);// 简化为p3=p1+p2;print(p3);
}
左移运算符(<<)重载
作用:重载左移运算符配合友元可以实现输出自定义数据类型
#include<iostream>
using namespace std;class Person{friend ostream& operator<<(ostream& out,Person& p);public:Person (int a,int b){m_a=a;m_b=b;}private:int m_a;int m_b;
};// operator<<(cout,p) -> cout << p
ostream& operator<<(ostream& out,Person& p){out<<p.m_a<<' '<<p.m_b;return out;
}int main(){Person p1(1,2);cout<<p1<<endl;return 0;
}
递增运算符重载
通过重载递增运算符,实现自己的整型数据
#include<iostream>
using namespace std;class MyInteger{friend ostream& operator<<(ostream& out,MyInteger myint);public:MyInteger(){m_num=0;}// 注意:返回的是参数的引用 防止++(++a)出错// 返回引用是为了一直对一个数据进行递加MyInteger& operator++(){cout<<this<<endl;this->m_num++;return *this;}MyInteger operator++(int){ MyInteger temp;cout<<this<<" 1 "<<endl; temp=*this;this->m_num++;// 返回+1之前的值return temp;}private:int m_num;
};// 输出重载
ostream& operator<<(ostream& out,MyInteger myint){out<<myint.m_num;return out;
}int main(){MyInteger myint,myint1;cout<<myint<<endl;++(++myint);cout<<myint<<endl;return 0;
}
赋值运算符重载(存在坑:堆区内存重复释放–深浅拷贝)
#include<iostream>
using namespace std;class Person{public:Person(int age){m_age=new int(age);} ~Person(){if(m_age!=NULL){delete m_age;m_age=NULL;}}// 注意: 函数参数为引用Person& operator=(Person& p){// 浅拷贝 ---释放对象时会有重复释放的问题// m_age=p.m_age;//深拷贝if(m_age!=NULL){delete m_age;m_age=NULL;}m_age=new int(*p.m_age);// 防止连续赋值出现问题:a=b=creturn *this;}int *m_age;
};ostream& operator<<(ostream& out,Person& p){out<<*p.m_age;return out;
}int main(){Person p1(10),p2(20),p3(30);cout<<p1<<' '<<p2<<' '<<p3<<endl;p1=p2=p3;cout<<p1<<' '<<p2<<' '<<p3<<endl;return 0;
}
关系运算符重载
#include<iostream>
using namespace std;class Person{public:Person(string name,int age){m_Name=name;m_Age=age;}string m_Name,m_Age;bool operator==(Person& p){ if ((m_Age==p.m_Age) && (m_Name==p.m_Name)){return true;}else{return false;}}bool operator!=(Person& p){if ((m_Age==p.m_Age) && (m_Name==p.m_Name)){return false;}else{return true;}}
};int main(){Person p1("lili",10),p2("lili",10);Person p3("lli",10),p4("lili",10);cout<<(p1==p2)<<endl;cout<<(p3!=p4)<<endl;return 0;
}
函数调用运算符重载(仿函数)—匿名函数对(classs())
- 函数调用运算符 () 也可以重载
- 由于重载后使用的方式非常像函数的调用,因此称为 仿函数
- 仿函数没有固定写法,非常灵活
黑马程序员C++学习笔记(第二阶段核心:面向对象)(二)相关推荐
- 黑马程序员Maven学习笔记
前言 这里是黑马程序员Maven学习笔记分享,这是视频链接. 我还有其它前端内容的笔记,有需要可以查看. 文章目录 前言 基础 Maven简介 Maven是什么 Maven的作用 Maven的下载 M ...
- 【C++】黑马程序员 C++学习课程—C++核心编程
[C++]黑马程序员 C++学习课程-C++核心编程 黑马程序员 C++学习课件, 为自用版本,单纯为了学习和查找资料更加方便 本阶段主要针对C++面向对象编程技术做详细讲解,探讨C++中的核心和精髓 ...
- 黑马程序员C++学习笔记(第三阶段核心:STL)--- 更新中
目录 迭代器 序列式容器 vector -- 可随机访问 list -- 不支持随机访问 deque -- 动态 关联式容器 -- 红黑树 map multimap set multiset -- 废 ...
- 黑马程序员Javaweb学习笔记01
该博客主要记录在学习黑马程序员Javaweb过程的一些笔记,方便复习以及加强记忆 文章目录 一 . BS架构,HTTP协议 http请求数据格式和相应数据格式 二 . web服务器 2.1 tomca ...
- 黑马程序员Javaweb学习笔记02【request和response】
该博客主要记录在学习黑马程序员Javaweb过程的一些笔记,方便复习以及加强记忆
- 黑马程序员C++学习笔记<第一阶段_基础篇>
配套视频网址: 黑马程序员:http://yun.itheima.com/course/520.html?bili B站:https://www.bilibili.com/video/BV1et411 ...
- 黑马程序员SSM-MyBatisPlus学习笔记
目录 一.MyBatisPlus简介 1.1 SpringBoot整合MyBatisPlus入门程序 1.2 MyBatisPlus概述 二.标准数据层开发 2.1 标准数据层CRUD功能 2.2 分 ...
- 黑马程序员C++学习笔记(第二阶段核心:面向对象)(一)
目录 内存分区:代码区,全局区,栈区,堆区 程序运行前:代码区,全局区 程序运行后:栈区,堆区 引用--给变量起别名 注意事项 引用做函数参数 引用做函数返回值 引用的本质---指针常量(值可变,指向 ...
- 【黑马程序员JVM学习笔记】01.引言
1. 什么是JVM? 定义: Java Virtual Machine,Java程序的运行环境(Java二进制字节码的运行环境) 好处: 一次编写,到处运行 自动内存管理,垃圾回收功能 数组下标越界检 ...
- day30 | 黑马程序员Java全程笔记 | 第二阶段MySQL高级 JDBC
01.反馈回顾 事务: ★ 概述: 逻辑上的一组操作,组成这组操作的各个单元要么同时成功,要么同时失败. 事务是一个最小的执行单元. mysql中的事务控制: 手动事务: 需要手动开启,提交,回滚开启 ...
最新文章
- matcaffe的blob维度顺序
- MAT之NSL:CPK_NN神经网络实现预测哪个样本与哪个样本处在同一层,从而科学规避我国煤矿突水灾难
- Linux 内核的文件 Cache 管理机制介绍
- QQ抢车位外挂(起始篇)--小研究成果展示
- Python基础---注意事项
- 浏览器相关功能系统调用
- Sublime Text 3 全程详细图文使用教程
- 最强大脑-高效记忆方法
- HTTP 的长连接和短连接
- php获取qq空间,使用php进行批量抓取QQ空间相册链接
- 辅流式沉淀池固体负荷计算方法_20000m3/d污水厂设计计算书
- No Silver Bullet: Essence and Accidents of Software | 没有银弹:软件开发中的主要问题和次要问题
- 美军回应网传UFO:视频为真 现有人类技术无法达到
- 仿《广州从化区慢性病地理信息平台》项目研发总结
- 浅谈对transforms.ToTensor()和transforms.Normalize()函数的理解
- 序数是什么意思_vlookup 列序数是什么意思
- 4342. 就一勾子 HDU1698 , kuangbin专题
- RuntimeError: PytorchStreamReader failed locating file data.pkl: file not found
- 使用idea编辑生产者与消费者
- JAVA版村庄哨塔种子_我的世界:5个奇特且罕见的种子,相邻的哨塔,循环的地形图...
热门文章
- Codeforces Round #168 (Div. 2)
- 问题六十八:着色模型(shading model)(1)——反射模型(reflection model)(3.1)——辐射学(Radiometry)
- 问题六十五:二叉查找树的一个应用实例——求解一元十次方程时单实根区间的划分
- PHP疗法,人体穴位疗法的秘密-咔咔养生网
- BI软件应用在哪些方面
- 提升大数据可视化的技巧有哪些
- 为何要搭建大数据分析平台
- Selenium处理Select控件
- android程序安装空间不足,AndroidStudio提示磁盘空间不足
- linux下怎么解压tar.xz,Linux下解压.tar.xz格式文件的方法