黑马程序员C++学习笔记(第二阶段核心:面向对象)(二)

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=c
            return *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())

  • 函数调用运算符 () 也可以重载
  • 由于重载后使用的方式非常像函数的调用,因此称为 仿函数
  • 仿函数没有固定写法,非常灵活
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值