目录
思维导图大纲:
思维方面:
1. 类的定义:
2. 类的特点:
3. this指针:
4. 类的默认成员函数
默认构造函数
1.构造函数
2.析构函数
3.拷贝构造函数
4. 赋值运算符重载
1. 运算符重载
5. 日期类实现:
Date.h
Date.cpp
Text.cpp
思维导图大纲:
思维方面:
思维方面:由面向过程转化为面向对象
我们造一辆车,我们需要金属框架,轮胎,玻璃窗,发动机等等
面向过程:我们去自己造金属框架,轮胎,玻璃窗,发动机等等,然后组装
面向对象:我们自己不造以上配件,我们直接组装
1. 类的定义:
1. C语言中的struct结构体
我们只能在其中放变量
更改更方便的名字需要借助typedef
typedef struct Stack
{
int* array;
int capacity;
int size;
}Stack;
2. C++中的class类
我们可以在其中存放变量,我们将这些变量叫做成员变量
成员变量前最好加_下划线来区分
我们还可以存放函数,我们将这些函数叫做成员函数
我们不用使用typedef直接使用类名
class Stack
{
// 成员函数
// 初始化
void Init();
// 销毁
void Destroy();
// 成员变量
int* _array;
int _capacity;
int _size;
};
2. 类的特点:
(1) 类域
我们之前了解了命名空间域,全局域,局部域
在类里面也被类域保护,声明和定义分离时需要表明
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-保护
想要访问类需要将权限改为公有,但默认我们不去修改类的成员变量,所以我们只将成员函数设为公有
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;
};
类的实列化:
前面的类只是声明,我们需要定义-即开空间
说人话就是:前面的类就好比一张图纸,我们实例化就是根据这张图纸建筑房子
类和对象的关系是:一对多,一张图纸可以盖很多很多相同的房子
int main()
{
// 类的实列化
Stack st;
st.Init();
st.Destroy();
return 0;
}
对象的大小计算和c语言的结构体的内存对齐规则一模一样
首先我们的类中的成员函数会被多次调用,不同的对象都会去调用,所以我们为了节省空间
不将成员函数放在类中存储,我们将成员函数放在公共区域
类中存储的只有成员变量
类内部没有成员变量的,只有成员函数或者什么都没有默认给一个字节用于对象占位标识,表示存在
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指针:
不存放在类,存放在栈,有些编译器也存放在寄存器中
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指针的题目
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
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.析构函数
函数名与类名一样,但是需要在最前面加上~取反符号,无返回值
析构函数我们不去实现编译器也会自动默认生成,
对于动态内存申请的空间我们需要自己实现析构函数手动释放
对于没有动态申请的类,我们不用实现析构函数,编译器默认生成的析构函数即可完成
// 析构函数
~Stack()
{
cout << "~Stack()::析构函数" << endl;
free(_array);
_array = nullptr;
_capacity = _size = 0;
}
3.拷贝构造函数
拷贝构造函数是构造函数的重载,函数一致,参数不同
对于表面的值拷贝属于浅拷贝,我们不用实现,编译器自动生成的即可
对于像栈一类的拷贝属于深拷贝,我们需要自己实现,另外开辟空间,将值拷贝到新的空间上
// 拷贝构造函数
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@
.* :: sizeof ?: .
• 注意以上5个运算符不能重载。
• 重载操作符⾄少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: int
operator+(int x, int y)
• ⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义,⽐如Date类重载operator-就有意义,但是重载operator+就没有意义。
• 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,⽅便区分。
5. 日期类实现:
在日期类实现当中,我们会去应用类的默认构造函数,以及运算符重载
Date.h
#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
#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
#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;
}