IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++基础(四):面向对象模型初探(成员变量和函数的存储、this指针、友元) -> 正文阅读

[C++知识库]c++基础(四):面向对象模型初探(成员变量和函数的存储、this指针、友元)

面向对象模型初探

1.1 成员变量和函数的存储

在这里插入图片描述

  • 在c语言中,“分开来声明的,也就是说,语言本身并没有支持“数据”和“函数”之间的关联性,我们把这种程序方法称为“程序性的”,由一组“分布在各个以功能为导航的函数中”的算法驱动,它们处理的是共同的外部数据。

  • c++实现了“封装”,那么数据(成员属性)和操作(成员函数)是什么样的呢?
    “数据”和“处理数据的操作(函数)”是分开存储的。
    c++中的非静态数据成员直接内含在类对象中,就像c struct一样。
    成员函数(member function)虽然内含在class声明之内,却不出现在对象中。
    每一个非内联成员函数(non-inline member function)只会诞生一份函数实例.
    C++类对象中的变量和函数是分开存储。

class MyClass01{
public:
	int mA;
};

class MyClass02{
public:
	int mA;
	static int sB;
};

class MyClass03{
public:
	void printMyClass(){
		cout << "hello world!" << endl;
	}
public:
	int mA;
	static int sB;
};

class MyClass04{
public:
	void printMyClass(){
		cout << "hello world!" << endl;
	}
	static void ShowMyClass(){
		cout << "hello world!" << endl;
	}
public:
	int mA;
	static int sB;
};

int main(){

	MyClass01 mclass01;
	MyClass02 mclass02;
	MyClass03 mclass03;
	MyClass04 mclass04;

	cout << "MyClass01:" << sizeof(mclass01) << endl; //4
	//静态数据成员并不保存在类对象中
	cout << "MyClass02:" << sizeof(mclass02) << endl; //4
	//非静态成员函数不保存在类对象中
	cout << "MyClass03:" << sizeof(mclass03) << endl; //4
	//静态成员函数也不保存在类对象中
	cout << "MyClass04:" << sizeof(mclass04) << endl; //4

	return EXIT_SUCCESS;
}

1.2 this指针

1.2.1 this指针工作原理

通过上例我们知道,c++的数据和操作也是分开存储,并且每一个非内联成员函数(non-inline member function)只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢

  • c++通过提供特殊的对象指针,this指针,解决上述问题。This指针指向被调用的成员函数所属的对象
  • c++规定,this指针是隐含在对象成员函数内的一种指针。当一个对象被创建后,它的每一个成员函数都含有一个系统自动生成的隐含指针this,用以保存这个对象的地址,也就是说虽然我们没有写上this指针,编译器在编译的时候也是会加上的。因此this也称为“指向本对象的指针”,this指针并不是对象的一部分,不会影响sizeof(对象)的结果。
  • this指针是C++实现封装的一种机制,它将对象和该对象调用的成员函数连接在一起,在外部看来,每一个对象都拥有自己的函数成员。一般情况下,并不写this,而是让系统进行默认设置。
    this指针永远指向当前对象。

成员函数通过this指针即可知道操作的是那个对象的数据。This指针是一种隐含指针,它隐含于每个类的非静态成员函数中。This指针无需定义,直接使用即可。
注意:静态成员函数内部没有this指针,静态成员函数不能操作非静态成员变量。

 c++编译器对普通成员函数的内部处理

在这里插入图片描述

1.2.2 this指针的使用

当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,可使用return *this.
this的链式编程(*this指向指针对象本身)
在这里插入图片描述
空指针访问成员函数是否保错,取决于成员函数中是否用到this指针

class Person{
public:
	//1. 当形参名和成员变量名一样时,this指针可用来区分
	Person(string name,int age){
		//name = name;
		//age = age; //输出错误
		this->name = name;
		this->age = age;
	}
	//2. 返回对象本身的引用
	//重载赋值操作符
	//其实也是两个参数,其中隐藏了一个this指针
	Person PersonPlusPerson(Person& person){
		string newname = this->name + person.name;
		int newage = this->age + person.age;
		Person newperson(newname, newage);
		return newperson;
	}
	void ShowPerson(){
		cout << "Name:" << name << " Age:" << age << endl;
	}
public:
	string name;
	int age;
};

//3. 成员函数和全局函数(Perosn对象相加)
Person PersonPlusPerson(Person& p1,Person& p2){
	string newname = p1.name + p2.name;
	int newage = p1.age + p2.age;
	Person newperson(newname,newage);
	return newperson;
}

int main(){

	Person person("John",100);
	person.ShowPerson();

	cout << "---------" << endl;
	Person person1("John",20);
	Person person2("001", 10);
	//1.全局函数实现两个对象相加
	Person person3 = PersonPlusPerson(person1, person2);
	person1.ShowPerson();
	person2.ShowPerson();
	person3.ShowPerson();
	//2. 成员函数实现两个对象相加
	Person person4 = person1.PersonPlusPerson(person2);
	person4.ShowPerson();

	system("pause");
	return EXIT_SUCCESS;
}

1.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;
	}
	void ShowPerson(){
		cout << "ID:" << mID << " mAge:" << mAge << endl;
	}
private:
	int mAge;
	mutable int mID;		//当成员变量类型符前用mutable修饰时例外
};

int main(){

	Person person;
	person.sonmeOperate();
	person.ShowPerson();

	system("pause");
	return EXIT_SUCCESS;
}

1.2.4 const修饰对象(常对象)

常对象只能调用const的成员函数
常对象可访问数据成员,不能修改,除非成员用mutable修饰

class Person{
public:
	Person(){
		this->mAge = 0;
		this->mID = 0;
	}
	void ChangePerson() const{
		mAge = 100;
		mID = 100;
	}
	void ShowPerson(){
        this->mAge = 1000;
		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();
}

1.3 友元

类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问。但是,有时候需要在类的外部访问类的私有成员,怎么办?
解决方法是使用友元函数,友元函数是一种特权函数,c++允许这个特权函数访问私有成员。这一点从现实生活中也可以很好的理解:
比如你的家,有客厅,有你的卧室,那么你的客厅是Public的,所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去,但是呢,你也可以允许你的闺蜜好基友进去。
程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。

1.3.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;
}
[友元类注意]

1.友元关系不能被继承。
2.友元关系是单向的,类A是类B的朋友,但类B不一定是类A的朋友。
3.友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不一定是类A的朋友。

思考: c++是纯面向对象的吗?
如果一个类被声明为friend,意味着它不是这个类的成员函数,却可以修改这个类的私有成员,而且必须列在类的定义中,因此他是一个特权函数。c++不是完全的面向对象语言,而只是一个混合产品。增加friend关键字只是用来解决一些实际问题,这也说明这种语言是不纯的。毕竟c++设计的目的是为了实用性,而不是追求理想的抽象。
— Thinking in C++

1.3.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();
}

1.4 强化训练(数组类封装)

MyArray.h

#ifndef MYARRAY_H
	#define MYARRAY_H
	
	class MyArray{
	public:
		//无参构造函数,用户没有指定容量,则初始化为100
		MyArray();
		//有参构造函数,用户指定容量初始化
		explicit MyArray(int capacity);
		//拷贝构造
		MyArray(const MyArray &array);
		//析构
		~MyArray();

		//用户操作接口
		//根据位置添加元素
		void SetData(int pos, int val);
		//获得指定位置数据
		int GetData(int pos);
		//尾插法
		void PushBack(int val);
		//获得长度
		int GetLength();
		
		//[]运算符重载
		int& operator[](int index);

	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];
}
//拷贝构造函数
MyArray::MyArray(const MyArray &array){
	this->pAdress = new int[array.mCapacity];
	this->mCapacity = array.mCapacity ;
	this->mSize = array.mSize;
	for(int i=0;i<array.mSize;i++){
		this->pAdress[i] = array.pAdress[i];
	}
}
//析构函数
MyArray::~MyArray(){
	if (this->pAdress!=NULL){
		delete[] this->pAdress;
		this->pAdress=NULL;
	}
}

//根据位置添加元素
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;
}

//[]运算符重载
int& MyArray::operator[](int index){
	return this->pAdress[index];
}

TestMyArray.cpp
#include"MyArray.h"

void test(){
	//创建数组
	MyArray* array = MyArray myarray(50);
	
	//拷贝构造
	MyArray* arr1 = new MyArray(*array);
	MyArray arr2 = *array;//构造函数返回的本体
	//MyArray* arr4 = array ;//不会调用拷贝构造函数
	delete array; 
	
	//数组中插入元素
	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;
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-05 17:09:10  更:2021-08-05 17:11:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/9 9:00:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码