4.4.2.3 const修饰成员函数
- 用const修饰的成员函数时,const修饰this指针指向的内存区域,成员函数体内不可以修改本类中的任何普通成员变量,
- 当成员变量类型符前用mutable修饰时例外。
//const修饰成员函数 class Person{ public: Person(){ this->mAge = 0; this->mID = 0; } //在函数括号后面加上const,修饰成员变量不可修改,除了mutable变量 void sonmeOperate() const{ //this->mAge = 200; //mAge不可修改 this->mID = 10; //const Person* const tihs; } void ShowPerson(){ cout << "ID:" << mID << " mAge:" << mAge << endl; } private: int mAge; mutable int mID; }; int main(){ Person person; person.sonmeOperate(); person.ShowPerson(); system("pause"); return EXIT_SUCCESS; } |
4.4.2.4 const修饰对象(常对象)
-
- 常对象只能调用const的成员函数
- 常对象可访问 const 或非 const 数据成员,不能修改,除非成员用mutable修饰
class Person{ public: Person(){ this->mAge = 0; this->mID = 0; } void ChangePerson() const{ mAge = 100; mID = 100; } void ShowPerson(){ cout << "ID:" << this->mID << " Age:" << this->mAge << endl; } public: int mAge; mutable int mID; }; void test(){ const Person person; //1. 可访问数据成员 cout << "Age:" << person.mAge << endl; //person.mAge = 300; //不可修改 person.mID = 1001; //但是可以修改mutable修饰的成员变量 //2. 只能访问const修饰的函数 //person.ShowPerson(); person.ChangePerson(); } |
4.5 友元
类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问。但是,有时候需要在类的外部访问类的私有成员,怎么办?
解决方法是使用友元函数,友元函数是一种特权函数,c++允许这个特权函数访问私有成员。这一点从现实生活中也可以很好的理解:
比如你的家,有客厅,有你的卧室,那么你的客厅是Public的,所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去,但是呢,你也可以允许你的闺蜜好基友进去。
程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。
4.5.1 友元语法
- friend关键字只出现在声明处
- 其他类、类成员函数、全局函数都可声明为友元
- 友元函数不是类的成员,不带this指针
- 友元函数可访问对象任意成员属性,包括私有属性
class Building; //友元类 class MyFriend{ public: //友元成员函数 void LookAtBedRoom(Building& building); void PlayInBedRoom(Building& building); }; class Building{ //全局函数做友元函数 friend void CleanBedRoom(Building& building); #if 0 //成员函数做友元函数 friend void MyFriend::LookAtBedRoom(Building& building); friend void MyFriend::PlayInBedRoom(Building& building); #else //友元类 friend class MyFriend; #endif public: Building(); public: string mSittingRoom; private: string mBedroom; }; void MyFriend::LookAtBedRoom(Building& building){ cout << "我的朋友参观" << building.mBedroom << endl; } void MyFriend::PlayInBedRoom(Building& building){ cout << "我的朋友玩耍在" << building.mBedroom << endl; } //友元全局函数 void CleanBedRoom(Building& building){ cout << "友元全局函数访问" << building.mBedroom << endl; } Building::Building(){ this->mSittingRoom = "客厅"; this->mBedroom = "卧室"; } int main(){ Building building; MyFriend myfriend; CleanBedRoom(building); myfriend.LookAtBedRoom(building); myfriend.PlayInBedRoom(building); system("pause"); return EXIT_SUCCESS; } |
[友元类注意]
|
思考: c++是纯面向对象的吗?
如果一个类被声明为friend,意味着它不是这个类的成员函数,却可以修改这个类的私有成员,而且必须列在类的定义中,因此他是一个特权函数。c++不是完全的面向对象语言,而只是一个混合产品。增加friend关键字只是用来解决一些实际问题,这也说明这种语言是不纯的。毕竟c++设计的目的是为了实用性,而不是追求理想的抽象。 --- Thinking in C++ |
4.5.2 课堂练习
请编写电视机类,电视机有开机和关机状态,有音量,有频道,提供音量操作的方法,频道操作的方法。由于电视机只能逐一调整频道,不能指定频道,增加遥控类,遥控类除了拥有电视机已有的功能,再增加根据输入调台功能。 |
提示:遥控器类可作为电视机类的友元类。
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; mChannel = minChannel; } //打开电视机 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 test01(){ 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 test02(){ //创建电视 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(); } |
4.5 强化训练(数组类封装)
MyArray.h
#ifndef MYARRAY_H #define MYARRAY_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 |
MyArray.cpp
#include"MyArray.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; } } |
TestMyArray.cpp
#include"MyArray.h" void test(){ //创建数组 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; } |
4.6 运算符重载
4.6.1 运算符重载基本概念
运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。(运算符重载不能改变本来寓意,不能改变基础类型寓意)
运算符重载(operator overloading)只是一种”语法上的方便”,也就是它只是另一种函数调用的方式。 |
在c++中,可以定义一个处理类的新运算符。这种定义很像一个普通的函数定义,只是函数的名字由关键字operator及其紧跟的运算符组成。差别仅此而已。它像任何其他函数一样也是一个函数,当编译器遇到适当的模式时,就会调用这个函数。
语法:
定义重载的运算符就像定义函数,只是该函数的名字是operator@,这里的@代表了被重载的运算符。函数的参数中参数个数取决于两个因素。
|
[两个极端] 有些人很容易滥用运算符重载。它确实是一个有趣的工具。但是应该注意,它仅仅是一种语法上的方便而已,是另外一种函数调用的方式。从这个角度来看,只有在能使涉及类的代码更易写,尤其是更易读时(请记住,读代码的机会比我们写代码多多了)才有理由重载运算符。如果不是这样,就改用其他更易用,更易读的方式。 对于运算符重载,另外一个常见的反应是恐慌:突然之间,C运算符的含义变得不同寻常了,一切都变了,所有C代码的功能都要改变!并非如此,对于内置的数据类型的表示总的所有运算符是不可能改变的。我们不能重载如下的运算符改变其行为。 1 + 4; |
4.6.2 运算符重载碰上友元函数
友元函数是一个全局函数,和我们上例写的全局函数类似,只是友元函数可以访问某个类私有数据。
案例: 重载左移操作符(<<),使得cout可以输出对象。
class Person{ friend ostream& operator<<(ostream& os, Person& person); public: Person(int id,int age){ mID = id; mAge = age; } private: int mID; int mAge; }; ostream& operator<<(ostream& os, Person& person){ os << "ID:" << person.mID << " Age:" << person.mAge; return os; } int main(){ Person person(1001, 30); //cout << person; //cout.operator+(person) cout << person << " | " << endl; return EXIT_SUCCESS; } |