C++ ----类与对象(上)
Posted L_add
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ ----类与对象(上)相关的知识,希望对你有一定的参考价值。
类与对象
1.类的6个默认函数
任何一个类在什么都不写的情况下,都会自动生成下面6个默认成员函数
class Date{};
2.构造函数
2.1概念
构造函数是一个特殊的成员函数,名字与类相同,创建类类型对象时由编译器自动调用,保证每个数组成员都有一个合适的初始值,并且在对象的生命周期内只调用一次
2.2特性
构造函数虽然叫构造,但是它的主任务不是开空间创建对象,而是初始化对象
特征:
1、函数名与类名相同
2、无返回值
3、对象实例化时编译器自动调用对应的构造函数
class Date
{
//可能会忘记调用初始化
//C++解决-》构造函数
public:
//对象实例化的时候自动调用
//保证对想一定初始化
Date(int year, int month, int day)//构造函数
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2021, 5, 24);
return 0;
}
4、构造函数可以重载
Date(int year, int month, int day)//默认初始化
{
_year = year;
_month = month;
_day = day;
}
Date()//函数重载
{
//给定值初始化
_year = 2000;
_month = 1;
_day = 1;
}
//一般情况下,对象初始化分为两种:1、默认初始化2、给定值初始化
//合二为一 -》全缺省
Date(int year = 2000, int month = 1,int day = 1)
{
_year = year;
_month = month;
_day = day;
}
5、如果类中没有显示定义构造函数,C++编译器会自动生成一个无参的默认构造函数,一旦用户显示定义,编译器将不再生成
6、无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参的构造函数、全缺省函数、编译器默认生成的构造函数,都可以认为是默认成员参数。(不用传参就可以调用的)
7、C++把类型分成**内置类型(不处理)和自定义类型。**内置类型就是语法已经定义好的类型:如:int,char,double…。自定义类型就是我们使用class/struct/union自己定义的类型。编译器生成默认的构造函数会对自定义类型成员调用它的默认成员函数
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print();//随机值
return 0;
}
class A
{
public:
A(int a = 0)
{
_a = a;
}
void Print()
{
cout << _a << endl;
}
private:
int _a;
};
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
//cout << _aa.a << endl;//不能访问在类外面不能访问
_aa.Print();
}
private:
int _year;
int _month;
int _day;
A _aa;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
3.析构函数
3.1概念
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象的销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的清理工作
3.2特性
析构函数是特殊的成员函数
特征:
1、析构函数名是在类型前加上字符 ~
2、无参数无返回值
3、一个类有且只有一个析构函数**(不能重载)。若未显示定义,系统会生成默认的析构函数(内置类型不处理)**。
4、对象生命周期结束时,C++编译器系统会自动调用析构函数
class Date
{
public:
~Date()//不能重载
{
//资源的清理
//像Stack这样的类,析构函数具有重大意义
cout << "~Date" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
return 0;
}
注意:
1、类的析构函数调用完全按照构造函数调用的相反顺序进行调用
2、全局对象先于局部对象进行构造
3、静态对象先于普通对象进行构造
Date d1;
Date d2;
谁先析构?
因为对象是定义在函数中,函数调用会建立栈帧
栈帧中的对象构造和析构也要符合后进先出
d1先构造
d2后构造
d2先析构
d1后析构
扩展:
数据结构的栈和堆和内存分段区域(操作系统)的栈和堆有什么区别和联系?
答:
1、没有绝对联系,它们属于两个学科各自的命名一个是数据结构,一个是分段(一段内存的命名)
2、数据结构栈和系统分段栈(函数栈帧)中的对象都符合后进先出
4.拷贝构造函数
4.1概念
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用
4.2特征
1、拷贝构造函数是构造函数的一个重载形式
2、拷贝构造函数的参数只有一个且必须使用引用传参,使用传值传参会引发无穷递归调用
class Date
{
public:
Date(int year = 2000, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//必须是引用传参否则会引起无穷递归调用
Date(const Date& d)//防止d改变(权限缩小)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d4(d1);//拷贝构造
return 0;
}
3、若未显示定义,系统默认生成默认的拷贝构造参数。默认的拷贝构造参数对象内存存储按字节序完成拷贝。我们把这种拷贝叫浅拷贝或值拷贝
4、拷贝构造对内置类型会完成浅拷贝,对自定义类型会调它的拷贝函数完成深拷贝
5、像Stack这样需要释放内存的类需要深拷贝,浅拷贝会被析构两次,导致程序崩溃。
6、浅拷贝中,其中一个对象改变,会导致另一个对象改变
5.赋值操作符重载
5.1运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,具有返回值类型,函数名字以及参数列表,其返回值类型与参数列表和普通的函数类似。
让自定义把类型可以像内置类型一样使用运算符,需要哪个就重载哪个运算符。
class Date
{
public:
Date(int year = 2000, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date operator = (const Date&d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2021,5,28);
Date d2;
Date d3;
//也是拷贝行为,但是不一样的是,拷贝构造是创建一个对象时,拿同类对象初始化的拷贝
//这里的复制拷贝是这个对象已经存在,已被初始化,
d1 = d2;
d1 = d2 = d3;//连续赋值
//d1 = (d2 = d3) //需要有返回值
//d1 = d2
return 0;
}
函数原型:返回值类型 operator操作符(参数列表)
class Date
{
public:
/*
//d1->this d2->Date d
bool Equel(Date d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
*/
bool operator == (Date d2)
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
//当运算符时两个操作数时,第一个参数是左操作数,第二个是右操作数
//d1 < d2
bool operator <(Date x)
{
if (_year < x._year)
return true;
else if (_year == x._year)
{
if (_month < x._month)
return true;
else if (_month == x._month)
{
if (_day == x._day)
return true;
}
}
return false;
}
private:
int _year;
int _month;
int _day;
};
/*bool operator == (Date d1, Date d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}*/
int main()
{
Date d1;
Date d2;
//d1.Equel(d2);
//内置类型,语言层面支持运算符
//自定义类型,默认不支持,但是C++可以用运算符重载让类对象支持用某个运算符
//opreator == (d1, d2);//d1 == d2
d1.operator == (d2);
d1.operator<(d2);
cout << (d1 == d2) << endl;
return 0;
}
注意:
1、不能通过连接其他符号来创建新的操作符:eg: operator@
2、重载操作符必须有一个类类型或枚举类型的操作数
3、用于内置类型的操作符,其含义不能改变:eg:内置整形+ ,不能改变含义
4、作为类成员的重载函数时,其形参有一个默认形参this,限定为第一个形参
5、.*、::、sizeof、?:、.以上五个运算符不能重载
运算符重载跟函数重载:
1、函数重载时支持定义同名函数
2、运算符重载时为了让自定义类型可以像内置类型一样去使用运算符
Date d4(d1);//拷贝构造
d1 = d2;//赋值重载 两个已经存在的对象拷贝
Date d5 = d1;
//拷贝构造 拿一个已经存在的对象去构造初始化另一个要创建的对象
6.日期类实现
Date.h
#include <iostream>
#include <assert.h>
using std::cout;
using std::cin;
using std::endl;
class Date
{
public:
Date(int year = 2000, int month = 1, int day = 1);
void Print();
//析构、拷贝构造。赋值重载、可以不写,默认生成
Date& operator+=(int day);
Date& operator-=(int day);
Date operator+(int day);
Date operator-(int day);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
bool operator >(const Date&d);
bool operator <(const Date&d);
bool operator >=(const Date&d);
bool operator <=(const Date&d);
bool operator ==(const Date&d);
bool operator !=(const Date&d);
int operator-(const Date&d);
private:
int _year;
int _month;
int _day;
};
Date.cpp
#include "Date.h"
inline int GetMonthDay(int year, int month)//频繁调用
{
//数组存储平年每个月的天数
int DayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day = DayArray[month];
if ((year % 4 == 0 && year % 400 != 0) || year % 400 == 0 && month == 2)
{
day = 29;
}
return day;
}
Date::Date(int year, int month, int day)
{
//检查日期的合法性
if (year >= 0
&&(month >0 && month < 13)
&& day <= GetMonthDay(year,month))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "非法日期" << endl;
assert(false);
}
}
void Date::Print()
{
cout << _year << "年" << _month << "月" << _day << "天" << endl;
}
Date& Date::operator+=(int day)
{
if (day < 0)
{
*this -= -day;
}
else
{
_day += day;
//天数不合法,不断进位
while (_day >= GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
++_year;
_month = 1;
}
}
}
return*this;
}
Date Date::operator+(int day)//复用
{
Date tmp(*this);
//复用operator+=
tmp += day;//tmp.operator+=(day)
return tmp;
}
Date& Date::operator-=(int day)
{
if (day < 0)
{
_day += -day;
}
else
{
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
}
return *this;
}
Date Date::operator-(int day)
{
Date tmp = *this;
//Date tmp(*this);
tmp -= day;//tmp.operator-=(&tmp.day)
return tmp;
}
Date& Date::operator++()
{
*this += 1;
return *this;
//++d->d.operator++(&d)
}
Date Date::operator++(int)
{
//d++->d.operator++(&d,0)
//int参数不需要给实参,没用,它的作用是为了跟前置++构成函数重载
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int )
{
Date tmp(*this);
*this -= 1;
return tmp;
}
//d1>d2->d1.operator>(&d1,d2)
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;
}
}
return false;
}
bool Date::operator ==(const Date&d)
{
return _year == d._year
&&_month == d._month
&&_day == d._day;
}
//复用
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);
}
bool Date::operator !=(const Date&d)
{
return !(*this == d);
}
int Date::operator-(const Date&d)
{
Date max = *t以上是关于C++ ----类与对象(上)的主要内容,如果未能解决你的问题,请参考以下文章