目录
1.const修饰成员变量
2.友元(类、成员函数、全局函数)
3.友元练习(电视类和遥控器类)
4.数组类封装强化
5.运算符重载
内容
#include
using namespace std;
#include
#include
/****************************************************************************************************
* 43. const 修饰成员函数:
* 用 const 修饰的成员函数时,const 修饰 this 指针指向的内存区域,成员函数体内不可以修改本类中的任何普通成员变
* 量,当成员变量类型符前用 mutable 修饰时例外。
*
* const 修饰对象(叫常对象):
* ① 常对象只能调用 const 修饰的成员函数;
* ② 常对象可访问 const 或非 const 数据成员,但是不能修改,除非成员用 mutable 修饰
****************************************************************************************************/
//const 修饰成员函数
class Person12{
public:
Person12(){ // 无参构造函数对成员变量赋初始值
this->mAge = 0;
this->mID = 0;
}
//在函数括号后面加上 const,修饰成员变量不可修改,除了 mutable 变量
void sonmeOperate() const{
//this->mAge = 200; //报错:mAge 不可修改
this->mID = 10; // 可修改,因为有mutable修饰
}
void ShowPerson(){
cout << "ID:" << mID << " mAge:" << mAge << endl;
}
public:
int mAge;
mutable int mID;
};
void test39()
{
/****** const 修饰成员函数 ******/
Person12 person01;
person01.sonmeOperate();
person01.ShowPerson();
/****** const 修饰对象 ******/
const Person12 person02;
//1. 可访问数据成员
cout << "Age:" << person02.mAge << endl;
//person02.mAge = 300; //不可修改
person02.mID = 1001; //但是可以修改 mutable 修饰的成员变量
//2. 只能访问 const 修饰的函数
//person02.ShowPerson();
person02.sonmeOperate();
}
/****************************************************************************************************
* 44. 友元——类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问。但是,有时候需要在类的外部访问
* 类的私有成员,怎么办?
* 解决方法是使用友元函数,友元函数是一种特权函数,C++允许这个特权函数访问私有成员。
* 这一点从现实生活中也可以很好的理解:
* > 比如你的家,有客厅,有你的卧室,那么你的客厅是 Public 的,所有来的客人都可以进去,但是你的卧室是私有的,
* 也就是说只有你能进去,但是呢,你也可以允许你的闺蜜好基友进去。程序员可以把一个全局函数、某个类中的成员函
* 数、甚至整个类声明为友元。
* 友元的语法:friend 关键字只出现在声明处,其他类、类成员函数、全局函数都可声明为友元;
* 友元全局函数:需要理解为:既是全局函数,也是某类的友元函数(某类的朋友),并非该类的成员函数,需要注意:
* ① 友元函数不是类的成员,不带 this 指针;
* ① 作用于哪个类(需要访问哪个类的私有数据),友元全局函数就需要在哪个类中声明,在全局定义(因为是全局函数);
* 友元成员函数:需要理解为:既是某类的成员函数,也是另外一个类的友元函数(类的朋友),需要注意:
* ② 友元函数可访问对象任意成员属性,包括私有属性。
* ③ 作用于哪个类(需要访问哪个类的私有数据),友元成员函数就需要在哪个类中声明,并在归属的类中再次声明,说明
* 归属性,最后在全局定义函数功能,因为在归属类内访问不到该函数友元类的私有数据;
* 友元类:需要理解为:自己本身是一个类(友元类),也是另外一个类的友元类(本类),注意:
* ① 此时,本类中的所有成员(变量和函数)都可以访问被友元类中的成员函数所访问,
* ① 友元关系不能被继承。
* ② 友元关系是单向的,类 A 是类 B 的朋友,但类 B 不一定是类 A 的朋友。
* ③ 友元关系不具有传递性。类 B 是类 A 的朋友,类 C 是类 B 的朋友,但类 C 不一定是类 A 的朋友。
* 思考: C++是纯面向对象的吗?
* 如果一个类被声明为 friend,意味着它不是这个类的成员函数,却可以修改这个类的私有成员,而且必须列在类的定义
* 中,增加 friend 关键字只是用来解决一些实际问题,因此友元是一个特权函数,所以C++不是完全的面向对象语言,而
* 只是一个混合产品。毕竟C++设计的目的是为了实用性,而不是追求理想的抽象。
****************************************************************************************************/
class Building; // 向前声明:先声明普通类,便于友元成员函数声明时识别该类
//定义Building的友元类Myfriend
class MyFriend{
public:
//友元成员函数,在此声明是确定友元函数的归属性
void LookAtBedRoom(Building& building);
void PlayInBedRoom(Building& building);
};
class Building{ // 类的定义,注意两个必须一个声明一个定义,不能重复定义
//全局函数做友元函数,在类中声明
friend void CleanBedRoom(Building& building);
//这是属于MyFriend类,作用于Building类的,友元成员函数,需要在Building类中声明
friend void MyFriend::LookAtBedRoom(Building& building);
//声明为Building的友元类,这个类在这段代码中并没有起多大作用
friend class MyFriend;
public:
Building(); // 这是类内无参构造函数,可以在类中声明在类外定义,也可以在类内声明并定义,此句用的是前者
// Building(){ // 在类中声明并定义
// this->mSittingRoom = "客厅";
// this->mBedroom = "卧室";
// }
public:
string mSittingRoom; // 客厅,公共空间
private:
string mBedroom; // 卧室,私有空间
};
//这是属于MyFriend类,作用于Building类的,友元成员函数,需要在类外定义
// 接上句,即该函数既是类Myfriend的成员函数,也是类Building的友元函数
void MyFriend::LookAtBedRoom(Building& building){
cout << "其他类的成员函数作为友元参观了……" << building.mBedroom << endl;
}
// 注意:该函数只是类Myfriend的成员函数,并非类Building的友元函数,但是由于Myfriend是Building的友元类,
// 所以该函数才有权访问Building类中的私有成员。
void MyFriend::PlayInBedRoom(Building& building){
cout << "其他类的成员函数作为友元在-" << building.mBedroom << "-玩耍了!" << endl;
}
//友元全局函数:该函数是全局函数,也是类Building的友元函数
void CleanBedRoom(Building& building){
cout << "友元全局函数打扫了——" << building.mBedroom << endl;
}
Building::Building(){
this->mSittingRoom = "我家客厅";
this->mBedroom = "我的卧室";
}
void test40()
{
Building building;
MyFriend myfriend;
CleanBedRoom(building);
myfriend.LookAtBedRoom(building);
myfriend.PlayInBedRoom(building);
}
/****************************************************************************************************
* 随堂练习05:请编写电视机类,电视机有开机和关机状态,有音量,有频道,提供音量调节和操作转换的方法。由于电视机只能逐一
* 调整频道,不能指定频道,增加遥控类,遥控类除了拥有电视机已有的功能外,再增加根据输入进行调台的功能。提示:遥控器可作
* 为电视机类的友元类。
****************************************************************************************************/
class Remote;
class Television{
friend class Remote;
public:
enum{ On, Off }; //电视状态
enum{ minVol, maxVol = 100 }; //音量从 0 到 100
enum{ minChannel = 1, maxChannel = 255 }; //频道从 1 到 255
Television(){ // 无参构造函数,给成员变量赋初始值,作为默认值
mState = Off; // 默认为关机
mVolume = minVol; // 默认为0
mChannel = minChannel; // 默认为1
}
//打开电视机
void OnOrOff(){
this->mState = (this->mState == On ? Off : On);
}
//调高音量
void VolumeUp(){
if (this->mVolume >= maxVol){ return; }
this->mVolume++;
}
//调低音量
void VolumeDown(){
if (this->mVolume <= minVol){ return; }
this->mVolume--;
}
//更换电视频道
void ChannelUp(){
if (this->mChannel >= maxChannel){ return; }
this->mChannel++;
}
void ChannelDown(){
if (this->mChannel <= minChannel){ return; }
this->mChannel--;
}
//展示当前电视状态信息
void ShowTeleState(){
cout << "开机状态:" << (mState == On ? "开机" : "关机") << endl;
if (mState == On){
cout << "当前音量:" << mVolume << endl;
cout << "当前频道:" << mChannel << endl;
}
cout << "-------------" << endl;
}
private:
int mState; //电视状态,开机,还是关机
int mVolume; //电视机音量
int mChannel; //电视频道
};
//电视机调台只能一个一个的调,遥控可以指定频道
//电视遥控器
class Remote{
public:
Remote(Television* television){ pTelevision = television; }
public:
void OnOrOff(){ pTelevision->OnOrOff(); }
//调高音量
void VolumeUp(){ pTelevision->VolumeUp(); }
//调低音量
void VolumeDown(){ pTelevision->VolumeDown(); }
//更换电视频道
void ChannelUp(){ pTelevision->ChannelUp(); }
void ChannelDown(){ pTelevision->ChannelDown(); }
//设置频道 遥控新增功能
void SetChannel(int channel){
if (channel < Television::minChannel || channel > Television::maxChannel){ return; }
pTelevision->mChannel = channel;
}
//显示电视当前信息
void ShowTeleState(){ pTelevision->ShowTeleState(); }
private:
Television* pTelevision;
};
//直接操作电视
void test41(){
Television television;
television.ShowTeleState();
television.OnOrOff(); //开机
television.VolumeUp(); //增加音量+1
television.VolumeUp(); //增加音量+1
television.VolumeUp(); //增加音量+1
television.VolumeUp(); //增加音量+1
television.ChannelUp(); //频道+1
television.ChannelUp(); //频道+1
television.ShowTeleState();
}
//通过遥控操作电视
void test42(){
//创建电视
Television television;
//创建遥控
Remote remote(&television);
remote.OnOrOff();
remote.ChannelUp();//频道+1
remote.ChannelUp();//频道+1
remote.ChannelUp();//频道+1
remote.VolumeUp();//音量+1
remote.VolumeUp();//音量+1
remote.VolumeUp();//音量+1
remote.VolumeUp();//音量+1
remote.ShowTeleState();
}
/****************************************************************************************************
* 随堂练习06:强化训练(数组类封装)
****************************************************************************************************/
/****** MyArray01.h 文件中 ******/
//#ifndef MYARRAY01_H
//#define MYARRAY01_H
class MyArray{
public:
//无参构造函数,用户没有指定容量,则初始化为 100
MyArray();
//有参构造函数,用户指定容量初始化
explicit MyArray(int capacity);
//用户操作接口
//根据位置添加元素
void SetData(int pos, int val);
//获得指定位置数据
int GetData(int pos);
//尾插法
void PushBack(int val);
//获得长度
int GetLength();
//析构函数,释放数组空间
~MyArray();
private:
int mCapacity; //数组一共可容纳多少个元素
int mSize; //当前有多少个元素
int* pAdress; //指向存储数据的空间
};
//#endif // MYARRAY01_H
/****** MyArray01.cpp 文件中 ******/
//#include"MyArray01.h" // 引入头文件
MyArray::MyArray(){
this->mCapacity = 100;
this->mSize = 0;
//在堆开辟空间
this->pAdress = new int[this->mCapacity];
}
//有参构造函数,用户指定容量初始化
MyArray::MyArray(int capacity){
this->mCapacity = capacity;
this->mSize = 0;
//在堆开辟空间
this->pAdress = new int[capacity];
}
//根据位置添加元素
void MyArray::SetData(int pos, int val){
if (pos < 0 || pos > mCapacity - 1){ return; }
pAdress[pos] = val;
}
//获得指定位置数据
int MyArray::GetData(int pos){ return pAdress[pos]; }
//尾插法
void MyArray::PushBack(int val){
if (mSize >= mCapacity){ return; }
this->pAdress[mSize] = val;
this->mSize++;
}
//获得长度
int MyArray::GetLength(){ return this->mSize; }
//析构函数,释放数组空间
MyArray::~MyArray(){
if (this->pAdress != nullptr){ delete[] this->pAdress; }
}
/****** 测试文件中 文件中 ******/
//#include"MyArray.h
void test43(){
//创建数组
MyArray myarray(50);
//数组中插入元素
for (int i = 0; i < 50; i++){
//尾插法
myarray.PushBack(i);
//myarray.SetData(i, i);
}
//打印数组中元素
for (int i = 0; i < myarray.GetLength(); i++){
cout << myarray.GetData(i) << " ";
}
cout << endl;
}
/****************************************************************************************************
* 45.运算符重载(operator overloading):即对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。运算
* 符重载只是一种“语法上的方便”,也就是说,它只是另一种函数调用的方式。
* 在 C++中,可以定义一个处理类的新运算符。这种定义很像一个普通的函数定义,只是函数的名字由关键字 operator 及其
* 紧跟的运算符组成。差别仅此而已。它的本质也是一个函数(同其他函数),当编译器遇到适当的模式时,就会调用这个函数。
* 语法:定义重载的运算符就像定义函数,只是该函数的名字是 “operator运算符” 。函数的参数中,参数个数取决于两个因素:
* > 运算符是一元(一个参数)的还是二元(两个参数);
* > 运算符被定义为全局函数(对于一元是一个参数,对于二元是两个参数)还是成员函数(对于一元没有参数,对于二元是
* 一个参数——此时该类的对象用作左耳参数)。
* [注意两个极端]。它确实是一个有趣的工具。但是应该注意:
* > 有些人很容易滥用运算符重载:它仅仅是一种语法上的方便而已,是另外一种函数调用的方式。从这个角度来看,只有
* 在能使涉及类的代码更易写,尤其是更易读时(请记住,读代码的机会比我们写代码多多了)才有理由重载运算符。如果
* 不是这样,就改用其他更易用,更易读的方式。
* > 对于运算符重载,另外一个常见的反应是恐慌:突然之间,C运算符的含义变得不同寻常了,一切都变了,所有 C 代码
* 的功能都要改变!其实在C++中并非如此,对于内置的数据类型的表达式的运算符是不可能改变的。例如想重载 int类
* 型数据的+号)。
* 可以重载的运算符:几乎 C 中所有的运算符都可以重载,但运算符重载的使用时相当受限制的。特别是
* > 不能使用 C 中当前没有意义的运算符,例如用**求幂,因为C中求幂的运算符为^;
* > 不能改变运算符优先级;
* > 不能改变运算符的参数个数。
* > 最好不要改变运算符的本质意义,如把加法运算符重载成减法操作。
* 这样的限制有意义,否则,所有这些行为产生的运算符只会混淆而不是澄清寓意。(具体哪些运算符可以重载,哪些不可以重载
* 间下图)
****************************************************************************************************/
int main()
{
// test39();
// test40();
// test41();
// test42();
// test43();
return 0;
}