【C++】C++类和对象详解(上)

目录

思维导图大纲:

思维方面:

[1. 类的定义:](#1. 类的定义:)

[2. 类的特点:](#2. 类的特点:)

[3. this指针:](#3. this指针:)

[4. 类的默认成员函数](#4. 类的默认成员函数)

默认构造函数

1.构造函数

2.析构函数

3.拷贝构造函数

[4. 赋值运算符重载](#4. 赋值运算符重载)

[1. 运算符重载](#1. 运算符重载)

[5. 日期类实现:](#5. 日期类实现:)

Date.h

Date.cpp

Text.cpp


思维导图大纲:

思维方面:

思维方面:由面向过程转化为面向对象
我们造一辆车,我们需要金属框架,轮胎,玻璃窗,发动机等等
面向过程:我们去自己造金属框架,轮胎,玻璃窗,发动机等等,然后组装
面向对象:我们自己不造以上配件,我们直接组装

1. 类的定义:

1. C语言中的struct结构体
我们只能在其中放变量
更改更方便的名字需要借助typedef

cpp 复制代码
typedef struct Stack
{
	int* array;
	int capacity;
	int size;
}Stack;

2. C++中的class类
我们可以在其中存放变量,我们将这些变量叫做成员变量
成员变量前最好加_下划线来区分
我们还可以存放函数,我们将这些函数叫做成员函数
我们不用使用typedef直接使用类名

cpp 复制代码
class Stack
{
	// 成员函数
	
	// 初始化
	void Init();
	// 销毁
	void Destroy();


	// 成员变量
	int* _array;
	int _capacity;
	int _size;
};

2. 类的特点:

(1) 类域
我们之前了解了命名空间域,全局域,局部域
在类里面也被类域保护,声明和定义分离时需要表明

cpp 复制代码
class Stack
{
	// 成员函数

	// 声明在内
	// 初始化
	void Init(int n = 4);
	// 销毁
	void Destroy();


	// 成员变量
	int* _array;
	int _capacity;
	int _size;
};

// 定义在外
// 需要使用Stack::声明类域
void Stack::Init(int n)
{
	_array = (int*)malloc(sizeof(int) * n);
	if (_array == nullptr)
	{
		perror("malloc fail!");
		return;
	}

	_capacity = n;
	_size = 0;
}

(2)访问限定符
我们知道类的成员函数和成员变量属于类域内,当我们主函数去调用时访问权限不够
类的默认权限为私有------private
类的三大权限:public-公有,private---私有,protect-保护
想要访问类需要将权限改为公有,但默认我们不去修改类的成员变量,所以我们只将成员函数设为公有

cpp 复制代码
class Stack
{
public:
	// 成员函数
	// 初始化
	void Init(int n = 4)
	{
		_array = (int*)malloc(sizeof(int) * n);
		if (_array == nullptr)
		{
			perror("malloc fail!");
			return;
		}

		_capacity = n;
		_size = 0;
	}

	// 销毁
	void Destroy()
	{
		free(_array);
		_array = nullptr;
		_capacity = 0;
		_size = 0;
	}

private:
	// 成员变量
	int* _array;
	int _capacity;
	int _size;
};

类的实列化:
前面的类只是声明,我们需要定义-即开空间
说人话就是:前面的类就好比一张图纸,我们实例化就是根据这张图纸建筑房子
类和对象的关系是:一对多,一张图纸可以盖很多很多相同的房子

cpp 复制代码
int main()
{
	// 类的实列化
	Stack st;
	st.Init();
	st.Destroy();

	return 0;
}

对象的大小计算和c语言的结构体的内存对齐规则一模一样
首先我们的类中的成员函数会被多次调用,不同的对象都会去调用,所以我们为了节省空间
不将成员函数放在类中存储,我们将成员函数放在公共区域
类中存储的只有成员变量
类内部没有成员变量的,只有成员函数或者什么都没有默认给一个字节用于对象占位标识,表示存在

cpp 复制代码
class A
{
public:
	void Print()
	{
		cout << "void Print()" << endl;
	}

private:
	char _a;
	int _b;
	double _c;
};

class B
{
public:
	void Print()
	{
		cout << "void Print()" << endl;
	}
};

class C
{};

class D
{
public:
	void Print()
	{
		cout << "void Print()" << endl;
	}
private:
	A _d;
	int _e;
};

int main()
{
	// 实例化
	A a;
	B b;
	C c;
	D d;

	// 输出大小
	cout << sizeof(a) << endl;
	cout << sizeof(b) << endl;
	cout << sizeof(c) << endl;
	cout << sizeof(d) << endl;

	return 0;
}

3. this指针:

不存放在类,存放在栈,有些编译器也存放在寄存器中

cpp 复制代码
class Date
{
public:

	/*void Init(Date* this, int year = 1949, int month = 1, int day = 1)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}*/

	void Init(int year = 1949, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	/*void Print()
	{
		cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
	}*/

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	// 我们定义两个对象进行初始化
	Date d1;
	Date d2;
	d1.Init(2024, 7, 11);
	d2.Init(2024, 8, 1);

	d1.Print();
	d2.Print();

	// 为什么调用同一个函数,但是却给不同对象初始化不同值,这边也没传地址啊?
	// 其实默认传了地址,并且使用this指针对不同的对象进行了初始化
	// 但是this指针不可以显示成为参数,可以在函数内部显示使用和返回

	return 0;
}

考察理解this指针的题目

cpp 复制代码
class A
{
public:
	void Print()
	{
		cout << "void Print()" << endl;
	}
private:
	int _a;
};

class B
{
public:
	void Print()
	{
		cout << "void Print()" << endl;
		cout << this->_a << endl;
	}
private:
	int _a;
};

int main()
{
	A* a = nullptr;
	a->Print();

	B* b = nullptr;
	b->Print();

	// 以上两个程序语句分别是1.报错,2.运行崩溃,3.成功运行?
	
	return 0;
}

类A只是传过去nullptr但是this没有进行解引用和访问操作所以程序成功运行。

类B传过去nullptr并且进行了访问操作所以程序崩溃。

4. 类的默认成员函数

什么是默认成员函数,简单说我们不去实现,编译器也会默认生成这些成员函数

默认构造函数

下面以栈类介绍类的默认函数

1.构造函数

函数名与类名一样,无返回值
全缺省构造函数
我们不自己实现构造函数,编译器会默认生成一个无参构造函数,这个函数对内置类型不做处理,对自定义类型会去调用他们的构造函数
无参构造函数,全缺省构造函数,编译器默认生成的无参构造函数都可以称作为默认构造,不传参即可调用的构造为默认构造
无参构造函数,全缺省构造函数,编译器默认生成的无参构造函数三者只能存在一个
相当于Stack中的Init

cpp 复制代码
class Stack
{
public:
	// 无参构造函数
	/*Stack()
	{
		cout << "Stack():无参构造" << endl;
		_array = (int*)malloc(sizeof(int) * 4);
		if (nullptr == _array)
		{
			perror("malloc fail!");
			exit(-1);
		}

		_capacity = 4;
		_size = 0;
	}*/

	// 全缺省构造函数
	Stack(int n = 4)
	{
		cout << "Stack():全缺省构造函数" << endl;
		_array = (int*)malloc(sizeof(int) * n);
		if (nullptr == _array)
		{
			perror("malloc fail!");
			exit(-1);
		}

		_capacity = n;
		_size = 0;
	}
private:
	int* _array;
	int _capacity;
	int _size;
};

int main()
{
	Stack st;

	return 0;
}

2.析构函数

函数名与类名一样,但是需要在最前面加上~取反符号,无返回值
析构函数我们不去实现编译器也会自动默认生成,
对于动态内存申请的空间我们需要自己实现析构函数手动释放
对于没有动态申请的类,我们不用实现析构函数,编译器默认生成的析构函数即可完成

cpp 复制代码
// 析构函数
~Stack()
{
	cout << "~Stack()::析构函数" << endl;
	free(_array);
	_array = nullptr;
	_capacity = _size = 0;
}

3.拷贝构造函数

拷贝构造函数是构造函数的重载,函数一致,参数不同
对于表面的值拷贝属于浅拷贝,我们不用实现,编译器自动生成的即可
对于像栈一类的拷贝属于深拷贝,我们需要自己实现,另外开辟空间,将值拷贝到新的空间上

cpp 复制代码
	// 拷贝构造函数
	Stack(const Stack& s) // 使用const修饰放在修改来源
	{
		cout << "Stack(const Stack& s)::拷贝构造函数" << endl;
		_array = (int*)malloc(sizeof(int) * s._capacity);
		if (nullptr == _array)
		{
			perror("malloc fail!");
			exit(-1);
		}

		memcpy(_array, s._array, sizeof(int) * s._capacity);
		_capacity = s._capacity;
		_size = s._size;
	}

4. 赋值运算符重载

1. 运算符重载

• 当运算符被⽤于类类型的对象时,C++语⾔允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使⽤运算符时,必须转换成调⽤对应运算符重载,若没有对应的运算符重载,则会编译报错。

• 运算符重载是具有特名字的函数,他的名字是由operator和后⾯要定义的运算符共同构成。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。

• 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数,⼆元运算符有两个参数,⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数。

• 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数⽐运算对象少⼀个。

• 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。

• 不能通过连接语法中没有的符号来创建新的操作符:⽐如operator@

cpp 复制代码
.*    ::    sizeof     ?:     .

• 注意以上5个运算符不能重载。

• 重载操作符⾄少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: int

operator+(int x, int y)

• ⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义,⽐如Date类重载operator-就有意义,但是重载operator+就没有意义。

• 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,⽅便区分。

5. 日期类实现:

在日期类实现当中,我们会去应用类的默认构造函数,以及运算符重载

Date.h

cpp 复制代码
#pragma once
#include <iostream>
#include <stdbool.h>

using namespace std;

class Date
{
public:
	// 日期类默认构造函数
	Date(int year = 1949, int month = 10, int day = 1);

	// 没有资源申请不用实现析构函数
	// ~Date();

	// 打印日期
	void Print();

	// 日期类拷贝构造函数
	Date(const Date& d);

	// 获取某日期的天数
	int GetMonthDay(const Date& d)
	{
		static int arr_day[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };

		if (d._month == 2 && ((d._year % 4 == 0) && (d._year % 100 != 0) || (d._year % 400 == 0)))
		{
			return 29;
		}

		return arr_day[d._month];
	}

	// 运算符重载

	// d1 = d2 , return Date&类型
	Date& operator=(const Date& d);

	// d1+=day , return Date&类型
	Date& operator+=(int day);

	// d1+day, return Date类型
	Date operator+(int day);

	// d1-=day , return Date&类型
	Date& operator-=(int day);

	// d1-day, return Date类型
	Date operator-(int day);

	// d1 == d2 , return bool类型
	bool operator==(const Date& d);

	// d1 != d2 , return bool类型
	bool operator!=(const Date& d);

	// 前置++ 后置++

	// ++d1 -> d1.operator++(&d);
	Date& operator++();

	// d1++ -> d1.operator++(&d, 0);
	Date operator++(int);

	// 比较大小
	
	// >
	bool operator>(const Date& d);

	// <
	bool operator<(const Date& d);

	// >=
	bool operator>=(const Date& d);

	// <=
	bool operator<=(const Date& d);

	// 日期-日期 , return int -> 天数
	int operator-(const Date& d);

private:
	// 年/月/日
	int _year;
	int _month;
	int _day;
};

Date.cpp

cpp 复制代码
#include "Date.h"

// 日期类默认构造函数
Date::Date(int year, int month, int day) // 这边缺省参数声明和定义只需要在声明时显示写出即可
{
	cout << "日期类默认构造函数" << endl;
	_year = year;
	_month = month;
	_day = day;
}

// 打印日期
void Date::Print()
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

// 日期类拷贝构造函数
Date::Date(const Date& d)
{
	cout << "日期类拷贝构造函数" << endl;
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

// 运算符重载

// d1 = d2 , return Date类型
Date& Date::operator=(const Date& d)
{
	cout << "Date& Date::operator=(const Date& d)" << endl;
	_year = d._year;
	_month = d._month;
	_day = d._day;

	return *this;
}

// d1+=day , return Date&类型
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= (-day);
	}
	_day += day;
	while (_day > GetMonthDay(*this))
	{
		_day -= GetMonthDay(*this);
		++_month;
		if (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}

	return *this;
}

// d1+day, return Date类型
Date Date::operator+(int day)
{
	Date tmp = *this;
	tmp += day;

	return tmp;
}

// d1-=day , return Date&类型
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += (-day);
	}
	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			--_year;
			_month = 12;
		}
		_day += GetMonthDay(*this);
	}

	return *this;
}

// d1-day, return Date类型
Date Date::operator-(int day)
{
	Date tmp = *this;
	tmp -= day;

	return tmp;
}

// 前置++ 后置++

// ++d1 -> d1.operator++(&d);
Date& Date::operator++()
{
	*this += 1;
	return *this;
}

// d1++ -> d1.operator++(&d, 0);
Date Date::operator++(int)
{
	Date ret(*this);

	*this += 1;

	return ret;
}

// d1 == d2 , return bool类型
bool Date::operator==(const Date& d)
{
	return _year == d._year && _month == d._month && _day == d._day;
}

// d1 != d2 , return bool类型
bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

// 比较大小

// >
bool Date::operator>(const Date& d)
{
	if (_year > d._year)
	{
		return true;
	}
	else if (_year == d._year)
	{
		if (_month > d._month)
		{
			return true;
		}
		else if (_month == d._month)
		{
			if (_day > d._day)
			{
				return true;
			}
			else {
				return false;
			}
		}
		else {
			return false;
		}
	}
	else {
		return false;
	}
}

// <
bool Date::operator<(const Date& d)
{
	return !(*this >= d);
}

// >=
bool Date::operator>=(const Date& d)
{
	return *this > d || *this == d;
}

// <=
bool Date::operator<=(const Date& d)
{
	return !(*this > d);
}

// 日期-日期 , return int -> 天数
int Date::operator-(const Date& d)
{
	int flag = 1;
	Date max = *this;
	Date min = d;
	// 假设法
	if (*this < d)
	{
		min = *this;
		max = d;
		flag = -1;
	}
	int day = 0;
	while (min < max)
	{
		++min;
		++day;
	}

	return day * flag;

}

Text.cpp

cpp 复制代码
#include "Date.h"

void Text01()
{
	// 测试默认构造
	//Date d1;
	//d1.Print();

	//Date d2(2024, 7, 21);
	//d2.Print();

	// 测试拷贝构造函数
	//Date d1(2024, 7, 21);
	//d1.Print();

	//Date d2(d1);
	//d2.Print();

	//Date d2 = d1;
	//d2.Print();

	// 测试运算符重载(=)
	//Date d1(2024, 7, 21);
	//Date d2, d3;
	//d2 = d3 = d1;
	//d1.Print();
	//d2.Print();
	//d3.Print();

	// 测试运算符重载(+=)
	//Date d1(2024, 7, 21);
	//d1 += 50000;
	//d1.Print();

	// 测试运算符重载(+)
	//Date d1(2024, 7, 21);
	//Date d2(d1 + 50000);
	//d1.Print();
	//d2.Print();

	// 测试运算符重载(-=)
	//Date d1(2024, 7, 21);
	//d1 -= 50000;
	//d1.Print();

	// 测试运算符重载(-)
	//Date d1(2024, 7, 21);
	//Date d2(d1 - 50000);
	//d1.Print();
	//d2.Print();

	// 测试运算符重载(前置++)
	/*Date d1(2024, 7, 31);
	Date d2(++d1);
	d1.Print();
	d2.Print();*/
	
	// 测试运算符重载(后置++)
	//Date d1(2024, 7, 31);
	//Date d2(d1++);
	//d1.Print();
	//d2.Print();

	// 测试运算符重载(==)
	//Date d1(2024, 7, 31);
	//Date d2(2024, 7, 31);
	//bool ret = (d1 == d2);
	//cout << ret << endl;

	// 测试运算符重载(!=)
	//Date d1(2024, 7, 31);
	//Date d2(2024, 7, 21);
	//bool ret = (d1 != d2);
	//cout << ret << endl;

	// 测试运算符重载(>)
	//Date d1(2024, 7, 31);
	//Date d2(2024, 7, 21);
	//bool ret = (d1 > d2);
	//cout << ret << endl;

	// 测试运算符重载(>=)
	//Date d1(2024, 7, 31);
	//Date d2(2024, 7, 31);
	//bool ret = (d1 >= d2);
	//cout << ret << endl;

	// 测试运算符重载(<)
	//Date d1(2024, 7, 31);
	//Date d2(2024, 7, 31);
	//bool ret = (d1 < d2);
	//cout << ret << endl;

	// 测试运算符重载(<=)
	//Date d1(2024, 7, 31);
	//Date d2(2024, 7, 31);
	//bool ret = (d1 <= d2);
	//cout << ret << endl;

	// 测试运算符重载(日期-日期)
	//Date d1(1958, 7, 11);
	//Date d2(2024, 7, 21);
	//cout << d1 - d2 << endl;

}


int main()
{
	Text01();
	return 0;
}
相关推荐
I_Am_Me_1 小时前
【JavaEE初阶】线程安全问题
开发语言·python
运维&陈同学1 小时前
【Elasticsearch05】企业级日志分析系统ELK之集群工作原理
运维·开发语言·后端·python·elasticsearch·自动化·jenkins·哈希算法
金士顿3 小时前
MFC 文档模板 每个文档模板需要实例化吧
c++·mfc
ZVAyIVqt0UFji4 小时前
go-zero负载均衡实现原理
运维·开发语言·后端·golang·负载均衡
loop lee4 小时前
Nginx - 负载均衡及其配置(Balance)
java·开发语言·github
SomeB1oody4 小时前
【Rust自学】4.1. 所有权:栈内存 vs. 堆内存
开发语言·后端·rust
toto4124 小时前
线程安全与线程不安全
java·开发语言·安全
水木流年追梦5 小时前
【python因果库实战10】为何需要因果分析
开发语言·python
人才程序员6 小时前
QML z轴(z-order)前后层级
c语言·前端·c++·qt·软件工程·用户界面·界面
w(゚Д゚)w吓洗宝宝了6 小时前
C vs C++: 一场编程语言的演变与对比
c语言·开发语言·c++