【黑马程序员】C++封装、对象特性、友元


20240214

类和对象

  • C++面向对象三大特性:封装、继承、多台

  • c++中万事皆对象

封装

封装的意义

  • 将对象
#include <iostream>

using namespace std;

const double PI = 3.14;
//定义一个圆类
class Circle{
// 访问权限
public:
	// 属性
	// 半径
	int m_r;
	// 行为
	// 获取圆的周长
	double getPerimeter() {
		return 2*PI*m_r;
	}
};

int main(){
	// 使用类初始化一个对象
	Circle c;
	// 对象属性赋值
	c.m_r = 2;
	// 调用对象方法获取圆周长
	cout << "圆的周长:" << c.getPerimeter() << endl;
	return 0;
}
  • 控制访问权限

    • public 公共权限,成员在类内可以访问,类外也可以访问

    • protect 保护权限,成员在类内可以访问,类外不可以访问

    • private 私有权限,成员在类内可以访问,类外不可以访问

#include <iostream>
#include <string>

using namespace std;

class Person{
public:
	string name;
protected:
	int age;
private:
	string phone;
public:
	// 类内访问
	void func(){
		name="zs";
		age = 12;
		phone="12345";
	}
};

int main(){
	Person p;
    // 类外访问
	p.name = "ls";
	// error: 'age' is a protected member of 'Person'
	// p.age = 18;
	// error: 'phone' is a private member of 'Person'
	//p.phone = 20;
	return 0;
}

struct和class的区别

  • 唯一区别就是默认的访问权限不同

  • struct默认的访问全向为public权限

  • classs默认的访问全向为private权限

struct P1{
	int age;    // 默认为public权限
};

class P2 {
	int age;    // 默认为private权限
};

int main(){
	// 实例化struct类对象
	P1 p1;
	// 实例化class类对象
	P2 p2;
	p1.age = 1;
	// error: 'age' is a private member of 'P2'
	// p2.age = 2;
	return 0;
}

成员属性设置为私有

  • 优点:

    • 可以自己控制读写权限

    • 对于写权限可以判断数据的有效性

#include <iostream>
#include <string>

using namespace std;

class Person{
public:
	void setName(string name){
		m_name=name;
	}
	string getName(){
		return m_name;
	}
	void setAge(int age){
		// 判断数据有效性
		if (age > 100 || age < 0) {
			return;
		}
		m_age = age;
	}
	int getAge(){
		return m_age;
	}
private:
	string m_name;	// 可读可写
	int m_age;	// 可读
	string m_password;	// 不可读不可写
};

int main(){
	Person p1;
	p1.setName("zs");
	p1.getName();
	p1.setAge(18);
	p1.getAge();
	return 0;
}

封装练习

设计立方体类

  • 求出立方体的面积和体积
#include <iostream>

using namespace std;

class Cube {
public:
	void setEdge(int edge){
		m_edge = edge;
	}

	// 计算面积
	int calcArea() {
		return 6 * m_edge * m_edge;
	}
	// 计算体积
	int calcVolume() {
		return m_edge*m_edge*m_edge;
	}
	// 判断两个立方体是否相同
	bool isSameCube(int edge) {
		return m_edge=edge;
	}
private:
	int m_edge;
};

int main(){
	Cube c1;
	c1.setEdge(2);
	cout << "c1立方体的面积是:" << c1.calcArea() << endl;
	cout << "c1立方体的体积是:" << c1.calcVolume() << endl;
	return 0;
}

点和圆的关系

  • 设计一个圆类和点类,判断点和圆的关系

  • 圆头文件编写circle.hpp

#pragma once
#include "point.hpp"

class Circle{
public:
	void setRadio(int r, int x, int y);
	int getR();
	Point getPoint();
private:
	int m_r;	// 半径
	Point p;	// 圆心
};
  • 点头文件编写point.hpp
#pragma once

class Point {
public:
	void setX(int x);

	void setY(int y);
	int getX();
	int getY();
private:
	int m_x;	// 横坐标
	int m_y;	// 纵坐标
};
  • 源文件编写point_and_circle.cpp
#include <iostream>
#include <cmath>
#include "circle.hpp"
#include "point.hpp"

using namespace std;

void Point::setX(int x) {
	m_x = x;
}

void Point::setY(int y) {
	m_y = y;
}
int Point::getX() {
	return m_x;
}
int Point::getY() {
	return m_y;	
}

void Circle::setRadio(int r, int x, int y) {
	m_r = r;
	p.setX(x);
	p.setY(y);
}
int Circle::getR() {
	return m_r;
}
Point Circle::getPoint() {
	return p;
}

void judgePointAndCircleRelation(Circle& c, Point& p) {
	// 计算点到圆心的距离
	int d = sqrt((p.getX()-c.getPoint().getX())^2+(p.getY()-c.getPoint().getY())^2);
	int r = c.getR();
	if (d == r){
		cout << "点在圆上" << endl;
	} else if (d > r) {
		cout << "点在圆外" << endl;
	} else {
		cout << "点在圆外" << endl;
	}
}

int main() {
	Circle c;
	Point p;
	c.setRadio(2, 0 , 0);
	p.setX(10);
	p.setY(2);
	judgePointAndCircleRelation(c, p);
	return 0;
}

对象的初始化和清理

构造函数和析构函数

  • 由编译器自动调用

  • 如果不写构造函数和析构函数,编译器会自己提供空实现的构造函数和析构函数

构造函数

  • 主要用于创建对象时,为对象成 员赋值

  • 语法:类名() {}

    • 构造函数没有返回值,也不用写void

    • 函数名称和类名相同

    • 构造函数可以有参数,因此可以发生重载

    • 程序在调用对象时会自动调用构造函数,无需手动调用且只会调用一次

析构函数

  • 主要用于对象销毁时执行一些清理工作

  • 语法:~类名() {}

    • 析构函数没有返回值,也不用写void

    • 函数名称和类名相同,在名称前加上~

    • 构造函数不可以有参数,因此也不能发生重载

    • 程序在调用对象时会自动调用构造函数,无需手动调用且只会调用一次

#include <iostream>

using namespace std;

class Person {
public:
	Person() {
		cout << "construct" << endl;
	}
	~Person() {
		cout << "deconstruct" << endl;
	}
};


int main(){
	Person p;
	return 0;
}

构造函数的分类及调用

  • 按参数分类:有参构造、无参构造

  • 按类型分为:普通构造、拷贝构造

  • 按调用方式

    • 括号法

    • 显示法

    • 隐式转换法

#include <iostream>

using namespace std;

class Person{
public:
	Person(){
		cout << "无参构造" << endl;
	}
	Person(int age){
		m_age=age;
		cout << "有参构造" << endl;
	}
	Person(const Person& p){
		m_age=p.m_age;
		cout << "拷贝构造" << endl;
	}
	~Person(){
		cout << "析构函数" << endl;
	}
private:
	int m_age;
};

void test() {
	cout << "===括号法调用===" << endl;
	// 无参构造调用
	Person p;
	// 调用默认构造函数时不要加()
	// Person px() 编译器会将这行代码当成一个函数声明
	// Person px();
	// 有参构造调用
	Person p1(3);
	// 拷贝构
	Person p2(p1);
	cout << "===显示法调用===" << endl;
	Person pp1 = Person(1);
	Person pp2 = Person(pp1);
	// Person(10) 匿名对象,当前行执行完系统会立即回收掉匿名对象
	Person(10);
	// 不要利用拷贝构造函数初始化匿名对象,编译器会认为Person(p) ==> Person p(p),造成p变量重定义
	cout << "===隐式转换法===" << endl;
	Person p4 =10;	// 等价与Person p4=Person(10);
	Person p5 = p4;
}

int main() {
	test();
	return 0;
}

拷贝构造函数的调用时机

  • 使用一个已经创建完毕的对象来初始化另一个对象

  • 值传递的方式给函数参数传值

  • 以值方式返回局部对象

#include <iostream>

using namespace std;

class Person{
public:
	Person(){
		cout << "无参构造" << endl;
	}
	Person(int age){
		m_age=age;
		cout << "有参构造" << endl;
	}
	Person(const Person& p){
		m_age=p.m_age;
		cout << "拷贝构造" << endl;
	}
	~Person(){
		cout << "析构函数" << endl;
	}
private:
	int m_age;
};

// 使用一个已经创建的对象来初始化另一个新对象
void test01() {
	Person p(10);
	Person p1(p);
}

// 值传递方式给函数参数传值
void func(Person p) {

}

void test02() {
	Person p(10);
	func(p);
}

// 以值方式返回局部对象
Person doWork() {
	Person p(1);
	return p;
}

void test03() {
	Person p =doWork();
}


int main(){
	test01();
	test02();
	test03();
	return 0;
}

构造函数调用规则

  • 默认情况下,c++编译器至少会给一个类添加3个函数

    • 默认构造函数(无参函数体为空)

    • 默认析构函数(无参函数体为空)

    • 默认拷贝构造函数,对属性进行值拷贝

  • 构造函数调用规则

    • 如果自定义了有参构造函数,编译器不会在提供默认无参构造函数
    #include <iostream>
    
    using namespace std;
    
    class Person{
    public:
    	Person(int age){
    		m_age=age;
    		cout << "有参构造函数" << endl;
    	}
    	~Person(){
    		cout << "默认析构函数" << endl;
    	}
    	int m_age;
    };
    
    void test01(){
    	Person p; //no matching constructor for initialization of 'Person'
    }
    
    int main(){
    	test01();
    	return 0;
    }
    
    • 但是会提供默认拷贝构造
    #include <iostream>
    
    using namespace std;
    
    class Person{
    public:
    	Person(){
    		cout << "默认构造函数" << endl;
    	}
    	Person(int age){
    		m_age=age;
    		cout << "有参构造函数" << endl;
    	}
    	~Person(){
    		cout << "默认析构函数" << endl;
    	}
    	int m_age;
    };
    
    void test01(){
    	Person p;
    	p.m_age=18;
    	Person p1(p); //调用默认拷贝构造
    	cout << p1.m_age << endl;
    }
    
    int main(){
    	test01();
    	return 0;
    }
    
    • 如果自定义了拷贝构造函数,编译器不会在提供其它构造函数
#include <iostream>

using namespace std;

class Person{
public:
	Person(const Person& p){
		m_age=p.m_age;
		cout << "拷贝构造函数" << endl;
	}
	~Person(){
		cout << "默认析构函数" << endl;
	}
	int m_age;
};

void test01(){
	//no matching constructor for initialization of 'Person'
    Person p;
	Person p1(18);
}

int main(){
	test01();
	return 0;
}

深拷贝与浅拷贝

  • 如果类中有属性需要在对上开辟空间,一定要自己实现拷贝构造函数

浅拷贝

  • 简单的赋值拷贝操作

  • 浅拷贝存在问题:堆区的内存重复释放

#include <iostream>

using namespace std;

class Person{
public:
        Person(){
                cout << "默认构造" << endl;
        }
        Person(int age, int height){
                m_age = age;
                // 使用new关键字在堆上开辟
                m_height=new int(height);
                cout << "赋值拷贝构造" << endl;
        }
        ~Person(){
                if (m_height != NULL) {
                        delete m_height;
                }
                cout << "析构函数" << endl;
        }
        int m_age;
        int *m_height;
};

void test(){
        Person p(18, 170);
        // 调用默认拷贝构造函数,使用的是浅拷贝的方式,析构释放堆上的内存会出错
        Person p1(p);
}

int main(){
        test();
        return 0;
}

在这里插入图片描述

深拷贝

  • 在堆区重新申请空间,进行拷贝操作

  • 解决深拷贝,重复释放对上内存导致的panic

#include <iostream>

using namespace std;

class Person{
public:
	Person(){
		cout << "默认构造" << endl;
	}
	Person(int age, int height){
		m_age = age;
		// 使用new关键字在堆上开辟
		m_height=new int(height);
		cout << "赋值拷贝构造" << endl;
	}
	Person (const Person& p) {
		m_age=p.m_age;
		m_height = new int(*p.m_height);
	}
	~Person(){
		if (m_height != NULL) {
			delete m_height;
		}
		cout << "析构函数" << endl;
	}
	int m_age;
	int *m_height;
};

void test1() {
	Person p(18, 170);
        // 调用自定义实现的深拷贝构造函数
        Person p1(p);
}

int main(){
	test1();
	return 0;
}

初始化列表

  • C++提供初始化列表语法,用来初始化 属性

  • 语法:构造函数():属性1(值1),属性2(值2)...

#include <iostream>
#include <string>

using namespace std;

class Person{
public:
	// 写死了函数必须按这种方式传递
	Person():m_name("zs"),m_age(10){
		cout << "无参初始化列表" << endl;
	}
	// 按照参数方式传入
	Person(int age, string name):m_age(age),m_name(name) {
		cout << "有参初始化列表" << endl;
	}
	~Person(){
		cout << "析构函数" << endl;
	}
	int m_age;
	string m_name;
};

int main(){
	Person p;
	cout << "姓名:" << p.m_name << " 年龄:" << p.m_age << endl;
	Person p1(18, "ls");
	cout << "姓名:" << p1.m_name << " 年龄:" << p1.m_age << endl;
	return 0;
}

类对象作为类的成员

  • C++中的成员可以是另一个类的对象,我们称该成员为对象成员

  • 构造的顺序:先调用成员的构造函数,在调用本类的构造函数,析构与构造的顺序相反

#include <iostream>
#include <string>

using namespace std;

class Phone{
public:
	Phone(string name):pname(name){
		cout << "Phone构造函数" << endl;
	}
	~Phone(){
		cout << "Phone析构函数" << endl;
	}
	string pname;
};

class Person{
public:
	Person(string name, string pname):m_name(name),m_phone(pname){
		cout << "Person构造函数" << endl;
	}
	~Person() {
		cout << "Person析构函数" << endl;
	}
	string m_name;
	Phone m_phone;
};

void test(){
	Person p("zs", "iphone");
}

int main(){
	test();
	return 0;
}

静态成员

  • 静态成员就是在成员变量和成员函数前面加上static关键字

  • 静态成员也是有访问权限的

静态成员变量

  • 所有对象共享同一份数据

  • 在编译阶段分配内存

  • 类内声明,类外初始化

  • 访问方式:通过对象访问;通过类名访问

#include <iostream>

using namespace std;

class Person{
public:
        // 类内声明
        static int age;
private:
        // 静态成员也是有访问权限的
        static int a;
};

// 类外初始化,Person::告诉编译器这是这个作用域下的变量
int Person::age=100;
int Person::a = 12;

void test(){
        Person p;
        cout << p.age << endl;
        Person p1;
        p1.age=18;
        cout << p.age << endl;
}

void test01(){
        // 静态成员变量不属于某个对象,所有对象都共享同一份数据,因此静态成员变量有两种访问方式
        // 方式一
        Person p;
        cout << "通过对象访问:" << p.age << endl;
        // 方式二
        cout << "通过类名访问:" << Person::age << endl;
}

void test02(){
        // 静态成员也是有访问权限的
        // error: 'a' is a private member of 'Person'
        // cout << "访问静态私有变量访问不到" << Person::a << endl;
}

int main(){
        test();
        test01();
        test02();
        return 0;
}

静态成员函数

  • 所有对象共享同一个函数

  • 静态成员函数只能访问静态成员变量

#include <iostream>

using namespace std;

class Person{
public:
        // 静态成员函数
        static void func(){
                // 静态成员函数只能访问静态变量
                m_A = 1;
                // 不能区分非静态变量是属于那个对象的成员
                // error: invalid use of member 'm_B' in static member function
                // m_B = 2;
                cout << "static void func() 调用" << endl;
        }
        static int m_A;
        int m_B;
};
int Person::m_A=1;

// 静态成员函数调用方式
void test(){
        // 使用对象调用
        Person p;
        p.func();
        // 使用类名调用
        Person::func();
}

int main(){
        test();
        return 0;
}

C++ 对象模型和this指针

成员变量和成员函数分开存储

  • 空类占用内存大小为1,用来占位

  • 非静态成员变量属于类上的对象

  • 静态成员变量、非静态成员函数(非静态成员函数也是只有一份函数实例)、静态成员函数都不属于类上的对象

#include <iostream>

using namespace std;

class A {};

class B {
	// 非静态成员变量属于类上的对象
	int a;
};

class C {
	// 非静态成员变量属于类上的对象
	int a;
	// 静态成员变量不属于类的对象上
	static int b;
};

class D {
	// 非静态成员变量属于类上的对象
	int a;
	// 静态成员变量不属于类的对象上
	static int b;
	void func(){}
};


class E {
	// 非静态成员变量属于类上的对象
	int a;
	// 静态成员变量不属于类的对象上
	static int b;
	void func(){}
	static void func1(){}
};


void test(){
	A a;
	// 空对象占用空间大小:1
	// C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
	// 每个空对象也应该有一个独一无二的内存地址
	cout << "空类的大小是:" << sizeof(a) << endl;
}

void test1(){
	B b;
	cout << "只有一个int类型非静态成员变量的类的大小是:" << sizeof(b) << endl;
}

void test2(){
	C c;
	cout << "一个int类型非静态成员变量和一个int类型的静态成员变量的类的大小是:" << sizeof(c) << endl;
}

void test3(){
	D d;
	// 普通成员函数不属于类上的对象
	cout << sizeof(d) << endl;
}

void test4() {
	E e;
	// 静态成员函数不属于类上的对象
	cout << sizeof(e) << endl;
}

int main(){
	test();
	test1();
	test2();
	test3();
	test4();
	return 0;
}

this指针

  • this指针指向被调用成员函数所属的对象

  • this指针是隐含每一个非静态成员函数的一种指针

  • this指针不需要定义,直接使用即可

this 指针具有以下特点:

  • this 指针是一个指针,它存储对象的地址。

  • this 指针是一个常量指针,不能修改它所指向的地址。

  • this 指针在非静态成员函数内部是可用的,它允许你通过 this->(*this). 访问对象的成员变量和成员函数。

  • this 指针的类型是指向类类型的常量指针。例如,如果对象属于 Person 类,则 this 的类型是 Person* const

this指针用途

  • 当形参和成员变量同名时可以使用this指针来区分

  • 在类的非静态成员函数中返回对象本身,return *this;

#include <iostream>

using namespace std;

class Person{
public:
	Person(int age){
		// 使用this指针解决命名冲突
		this->age=age;
	}
	// 返回对象本身时需要使用引用类型
	// 如果返回的不是引用,那么外层调用位置在赋值时就会调用拷贝构造生成新的临时对象
	Person& PersonAddAge(Person& p) {
		this->age += p.age;
		return *this;
	}
	int age;
};

void test(){
	Person p(18);
	cout << p.age << endl;
}

void test1(){
	Person p1(10);
	Person p2(10);
	// 链式编程思想
	p1.PersonAddAge(p2).PersonAddAge(p2).PersonAddAge(p2).PersonAddAge(p2);
	cout << p1.age << endl;
}

int main(){
	test();
	test1();
	return 0;
}

空指针访问成员函数

  • 空指针是可以正常的访问成员函数的
#include <iostream>

using namespace std;

class Person{
public:
        void showClassName() {
                cout << "this is Person class" << endl;
        }

        void showPersonAge() {
                // 增加代码的健壮性
                ifthis == NULL{
                        return;
                }
                // 默认类中的成员属性都会有一个默认的this指针,即this->age
                cout << age << endl;
        }
        int age;
};

void test(){
        Person * p = NULL;
        p->showClassName();
        // p->showPersonAge() 空指针奔溃,因为内部使用的是this->age,而this是一个NULL
        p->showPersonAge();
}

int main(){
        test();
        return 0;
}

const修饰成员函数

常函数

  • 成员函数后加上const之后,我们称这个函数为常函数

  • 常函数内不可以修改成员属性

  • 成员属性声明时加上mutable后,在常函数中依然可以修改

常对象

  • 声明对象前加const称该对象为常对象

  • 常对象只能调用常函数

#include <iostream>

using namespace std;

class Person{
public:
	Person(){}
	// this指针相当于Person* const this,this指向的对象不能更改
	// 在函数后面加上const相当于 const Person* const this,this指向的对象和值都不能改
	void showPerson() const {
		// age = 10;
		// a有mutable修饰所以可以更改
		a = 1;
	}
	void func(){}
	int age;
	mutable int a;
};

// 常函数测试
void test(){
	Person p;
	p.showPerson();
}

// 常对象测试
void test1(){
	// 在对象前加const,变为常对象
        const Person p;
        // p.age =1;
	// a是mutable修饰的变量,在常对象下也可以修改
	p.a = 1;

	// 常对象只能调用常函数
	p.showPerson();
	// 常对象不能调用普通函数,因为在常对象限制了只能修改mutable修饰的变量,而在普通函数中可以修改任意变量
	// p.func();
}

int main(){
	test();
	test1();
	return 0;
}

友元

  • 友元的目的就是让一个类或者函数访问另一个类中的私有成员

使用全局函数做友元

#include <iostream>
#include <string>

using namespace std;

class Building{
	// 声明GoodGay为Building的友元函数,这样在GoodGay函数中可以访问Building的私有成员变量
	friend void GoodGay(Building& building);
public:
	Building(){
		sittingRoom = "sittingRoom";
		bedingRoom = "bedingRoom";
	}
public:
	string sittingRoom;
private:
	string bedingRoom;
};

// 全局函数想访问类的私有成员
void GoodGay(Building& building){
	cout << "Good Gay 正在访问:" << building.sittingRoom << endl;
	cout << "Good Gay 正在访问:" << building.bedingRoom << endl;
}

void test(){
	Building b;
	GoodGay(b);
}

int main(){
	test();
	return 0;
}

类做友元

  • 让友元类可以访问另一个类中的所有成员函数
#include <iostream>
#include <string>
using namespace std;

class Building;
class GoodGay {
public:
	GoodGay();
	// visit()函数访问Building中的属性
	void visit();
	Building* b;
};

class Building{
	// 声明GoodGay类是Building的友元类
	friend class GoodGay;
public:
	Building();
public:
	string sittingRoom;
private:
	string bedingRoom;
};

// 类外实现成员函数
GoodGay::GoodGay(){
	// 构造函数中创建一个Building的对象
	b=new Building;
}
void GoodGay::visit(){
	cout << "GoodGay class正在访问: " << b->sittingRoom << endl;
	cout << "GoodGay class正在访问: " << b->bedingRoom << endl;
}

Building::Building(){
	sittingRoom="sittingRoom";
    bedingRoom="bedingRoom";
}
// 测试
void test(){
	GoodGay gg;
	gg.visit();
}

int main(){
	test();
	return 0;
}

成员函数做友元

  • 让类中的某些成员函数可以访问另一个类中的私有成员变量
#include <iostream>
#include <string>
using namespace std;

class Building;
class GoodGay {
public:
	GoodGay();
	// visit()函数访问Building中的私有属性
	void visit();
	// visit1()函数不可以访问Building中的私有属性
	void visit1();
	Building* b;
};

class Building{
	// 声明GoodGay类下的visit函数作为Building类的友元函数
	friend void GoodGay::visit();
public:
	Building();
public:
	string sittingRoom;
private:
	string bedingRoom;
};

// 类外实现成员函数
GoodGay::GoodGay(){
	// 构造函数中创建一个Building的对象
	b=new Building;
}
void GoodGay::visit(){
	cout << "GoodGay visit正在访问: " << b->sittingRoom << endl;
	cout << "GoodGay visit正在访问: " << b->bedingRoom << endl;
}

void GoodGay::visit1(){
        cout << "GoodGay visit1正在访问: " << b->sittingRoom << endl;
}

Building::Building(){
	sittingRoom="sittingRoom";
        bedingRoom="bedingRoom";
}

void test(){
	GoodGay gg;
	gg.visit();
	gg.visit1();
}

int main(){
	test();
	return 0;
}

运算符重载

  • 对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

  • 对于内置的数据类型的表达式的运算符是不可能改变的

  • 不要滥用运算符重载

加号运算符重载

  • 可以计算自定义数据类型

成员函数实现运算符重载

  • 成员函数实现运算符重载的本质 p2.operator(p1)
#include <iostream>

using namespace std;

class Person{
public:
	Person(){}
	Person(int a, int b) {
		this->a=a;
		this->b=b;
	}
	// 成员函数实现运算符重载
	Person operator+(const Person& p) {
		Person temp;
		temp.a=this->a+p.a;
		temp.b=this->b+p.b;
		return temp;
	}
public:
	int a;
	int b;
};

void test() {
	Person p1(10,10);
	Person p2(10,10);
	// 成员函数实现运算符重载的本质 p2.operator(p1)
	Person p3 = p1+p2;
	cout << "p3.a: " << p3.a << endl;
	cout << "p3.b: " << p3.b << endl;
}

int main(){
	test();
	return 0;
}

全局函数实现运算符重载

  • 全局函数实现运算符重载的本质是operator+(p1,p2)
#include <iostream>

using namespace std;

class Person{
public:
	Person(){}
	Person(int a, int b) {
		this->a=a;
		this->b=b;
	}
public:
	int a;
	int b;
};

// 全局函数实现运算符重载
Person operator+(Person p1, Person p2) {
	Person temp;
	temp.a=p1.a+p2.a;
	temp.b=p1.b+p2.b;
	return temp;
}
// 全局函数实现函数重载

void test() {
	Person p1(10,10);
	Person p2(10,10);
	// 全局函数实现运算符重载的本质是operator+(p1,p2)
	Person p3 = p1+p2;
	cout << "p3.a: " << p3.a << endl;
	cout << "p3.b: " << p3.b << endl;
}

int main(){
	test();
	return 0;
}

全局函数实现函数重载

  • 通过函数重载实现不同类型的加法运算
#include <iostream>

using namespace std;

class Person{
public:
	Person(){}
	Person(int a, int b) {
		this->a=a;
		this->b=b;
	}
public:
	int a;
	int b;
};

// 全局函数实现函数重载
Person operator+(Person p1, int num) {
	Person temp;
	temp.a=p1.a+num;
	temp.b=p1.b+num;
	return temp;
}

void test() {
	Person p1(10,10);
	int num = 100;
	// 通过函数重载实现了Person和int类型的加法运算
	Person p3 = p1+num;
	cout << "p3.a: " << p3.a << endl;
	cout << "p3.b: " << p3.b << endl;
}

int main(){
	test();
	return 0;
}

左移运算符重载

  • 作用:可以输出自定义数据类型

  • 重载左移运算符配合友元可以实现输出自定义数据类型

#include <iostream>

using namespace std;

class Person{
	friend ostream& operator<<(ostream& cout, Person p);
public:
	Person(int a, int b){
		this->a = a;
		this->b = b;
	}
	// 成员函数重载左移运算符的本质是p<<cout
	// 在C++中一般不适用成员函数重载左移运算符,因为无法实现cout<<p的格式
	// operator<<(cout) {}
private:
	int a;
	int b;
};

// ostream& 返回cout这样可以在链式使用时继续追加
// 本质:operator<<(cout, p),简化为cout<<p
ostream& operator<<(ostream& cout, Person& p) {
	cout << "a= " << p.a <<" b= " << p.b;
	return cout;
}

void test(){
	Person p(1,1);
	cout << p << endl;
}

int main(){
	test();
	return 0;
}

递增运算符重载

  • 通过重载自增运算符,实现自己的整形数据
#include <iostream>

using namespace std;

class MyInt{
	friend ostream& operator<<(ostream& cout, MyInt i);
public:
	// 重载前置++运算符,返回引用为了一直对一个数据进行递增操作
	MyInt& operator++(){
		num++;
		return *this;
	}
	// 重载后置++运算符
	// void operator++(int) int代表占位参数,可以用于区分前置和后置递增
	MyInt operator++(int){
		MyInt temp = *this;
		num++;
		return temp;
	}
private:
	int num;
};

ostream& operator<<(ostream& cout, MyInt i){
	cout << i.num;
	return cout;
}

void test(){
	MyInt myint;
	cout << ++(++myint) << endl;
	MyInt myint1;
	cout << (myint++)++ << endl;
}

int main(){
	test();
	return 0;
}

赋值运算符重载

#include <iostream>

using namespace std;

class Person{
public:
	Person(int age) {
		this->age=new int(age);
	}
	Person& operator=(const Person& p) {
		// 编译器提供的是浅拷贝
		// age=p.age;
		// 先判断是否有属性在堆区,如果有先释放,然后在深拷贝
		if (age != NULL) {
			delete age;
			age=NULL;
		}
		// 深拷贝
		age=new int(*p.age);
		return *this;
	}
	~Person(){
		if (age == NULL) {
			return;
		}
		delete age;
		age=NULL;
	}
	int* age;
};

void test(){
	Person p1(18);
	Person p2(20);
	Person p3(30);
	p3=p2=p1;
	cout << *p1.age << endl;
	cout << *p2.age << endl;
	cout << *p3.age << endl;
}

int main(){
	test();
	return 0;
}

关系运算符重载

#include <iostream>

using namespace std;

class Person{
	friend ostream& operator<<(ostream& cout, Person& p);
public:
	Person(int age) {
		this->age=age;
	}
	bool operator==(const Person& p) {
		return age==p.age;
	}

	bool operator!=(const Person& p) {
		return age!=p.age;
	}
	bool operator>(const Person& p) {
		return age>p.age;
	}
	bool operator>=(const Person& p) {
                return age>=p.age;
        }
	bool operator<(const Person& p) {
                return age<p.age;
        }
	bool operator<=(const Person& p) {
                return age<=p.age;
        }
private:
	int age;
};

ostream& operator<<(ostream& cout, Person& p){
	cout << p.age << endl;
	return cout;
}

void test(){
	Person p1(18);
	Person p2(20);
	cout << (p1== p2) << endl;
	cout << (p1!= p2) << endl;
	cout << (p1>= p2) << endl;
	cout << (p1> p2) << endl;
	cout << (p1<= p2) << endl;
	cout << (p1< p2) << endl;
}

int main(){
	test();
	return 0;
}

函数调用运算符重载

  • 由于重载后的使用方式很像函数调用,因此也称为仿函数

  • 仿函数没有固定写法,非常灵活

#include <iostream>

using namespace std;

class Person{
public:
        // 打印类仿函数
        void operator()(string text) {
                cout << text << endl;
        }
};

class MyAdd{
public:
        // 加法类仿函数
        int operator()(int num1, int num2) {
                return num1+num2;
        }
};
 
void test(){
        Person p;
        // 由于使用起来非常类似于函数调用,因此也称为仿函数
        p("123");
}

void test1(){
        MyAdd myAdd;
        cout << myAdd(1,2) << endl;
        // 使用匿名函数调用
        cout <<MyAdd()(100,10) << endl;
}

int main(){
        test();
        test1();
        return 0;
}
  • 17
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

double_happiness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值