C++——类和对象的基本认识中

类的六个成员函数

构造函数

特点

  • 函数名与类名相同
  • 无返回值
  • 对象实例化时编译器自动调用对应的构造函数
  • 构造函数可以重载
  • 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
  • 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
    注意
  • 无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。
  • 一般情况下,有内置类型成员,就需要自己写构造函数,不能用编译器自己生成的。
  • 全部都是自定义类型成员,可以考虑让编译器自己生成
  • 如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
内置类型自定义类型
语言提供,无需用户定义用户根据需求定义
通常由编译器自动管理需要用户手动管理

自定义类型,默认缺省值,编译器自动生成

#include<iostream>
using namespace std;
class date
{
public:
	date(int year=2025,int month=03,int day=03)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << _year << endl;
	}

	void print()
	{
		cout << _year << " " << _month << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	date d1;
	d1.print();
	return 0;
}

在这里插入图片描述

无参调用,对象后面不能带括号

#include<iostream>
using namespace std;
class date
{
public:
	
	date()
	{
		_year = 1;
		_month = 2;
		_day = 3;
		cout << _year << endl;
	}
	void print()
	{
		cout << _year <<" "<<_month<< endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	date d1;
	d1.print();
	return 0;
}

半缺省调用

#include<iostream>
using namespace std;
class date
{
public:
	date(int year=2025,int month=03,int day=03)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << _year << endl;
	}
	
	void print()
	{
		cout << _year <<" "<<_month<< endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	date d1(2029);
	d1.print();
	return 0;
}

构造函数的重载

#include<iostream>
using namespace std;
class date
{
public:
	date(int year=2025,int month=03,int day=03)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << _year << endl;
	}
	date(double h=2.2)
	{
		cout << h << endl;
	}
	void print()
	{
		cout << _year <<" "<<_month<< endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	date d1(2029);
	d1.print();
	date d2(5.5);
	return 0;
}

析构函数

与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
特点

  • 析构函数名是在类名前加上字符 ~
  • 无参数无返回值类型。
  • 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  • 析构函数不能重载
  • 对象生命周期结束时,C++编译系统系统自动调用析构函数
  • 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类
  • 需要释放资源的都是自定义类型,不需要写析构
#include<iostream>
using namespace std;
class stack 
{
private:
	int* _a = nullptr;
	int _top = 0;
	int _capacity;
public:
	stack(int capacity=4)
	{
		cout << "stack()" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	

	~stack()
	{
		cout << "~~" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}
};

int main()
{
	stack st1;
	return 0;
}

在这里插入图片描述


构造函数与析构函数的调用顺序

  • 构造函数是按照语句的顺序进行构造,析构函数是按照与构造函数相反的顺序进行析构
  • 对象析构要在生存在作用域结束的时候析构
  • static改变对象的生存作用域,要等到程序结束时才会析构释放对象

假定已有A,B,C,D四个类的定义
C c;
int main()
{
A a;
B b;
static D d;
return 0;
}

它们的析构函数调用顺序是B,A,D,C。全局对象先于局部对象进行构造。局部对象按照出现的顺序进行构造,无论是否为static。所以构造的顺序为 c a b d。析构的顺序按照构造的相反顺序析构,只需注意static改变对象的生存作用域之后,会放在局部对象之后进行析构。因此析构顺序为B A D C。


拷贝构造

深拷贝与浅拷贝
深拷贝:深拷贝是指在拷贝对象时,为新对象分配独立的内存资源,并复制原对象的数据到新内存中。这样,新旧对象的资源是独立的,不会相互影响。深拷贝通常需要在拷贝构造函数中手动实现。
浅拷贝:默认的拷贝构造函数执行的是浅拷贝,即逐位复制对象的内存内容。对于包含指针等动态资源的成员变量,浅拷贝会导致新旧对象的指针指向相同的内存地址,这可能导致资源管理问题,如重复释放内存等。
在这里插入图片描述

拷贝构造函数是C++中用于初始化类的新对象的一个特殊构造函数,它以同类型的另一个对象为参数,用于复制对象的成员变量。
特点

  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个且必须是类类型对象的引用(const 类&),使用传值方式编译器直接报错,因为会引发无穷递归调用
  • 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
  • 在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的
  • 类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝
  • 没有返回值
#include<iostream>
using namespace std;
class date
{
public:
	date(int year = 2000, int month = 1, int day = 1)
	{
		cout << 1 << endl;
		_year = year;
		_month = month;
		_day = day;
	}
	date(const date& d)
	{
		cout << 2 << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	date d1;
	date d2(d1);
	return 0;
}

在这里插入图片描述

class Date
{
public:
	Date(int year, int minute, int day)
	{
		cout << "Date(int,int,int):" << this << endl;
	}
	Date(const Date& d)
	{
		cout << "Date(const Date& d):" << this << endl;
	}
	~Date()
	{
		cout << "~Date():" << this << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};


Date Test(Date d)
{
	Date temp(d);
	return temp;
}
int main()
{
	Date d1(2025, 1, 26);
	Test(d1);
	return 0;
}

在这里插入图片描述
在这里插入图片描述


运算符重载

它允许程序员为自定义类型(如类和结构体)定义运算符的行为。通过运算符重载,可以使得代码更加直观和易读。运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
运算符重载语法形式
返回类型 operator操作符(参数列表){}
特点

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数目少一,因为成员函数的第一个参数为隐藏的this
  • .* :: sizeof ?: . 这五个运算符不能重载

赋值运算符重载

语法形式
类名& operator=(const 类名& 右值);

  • 赋值运算符只能重载成类的成员函数不能重载成全局函数
  • 默认成员函数只能写在类里面,不能写到全局,因为会自动生成,但可以在类里面声明。
  • 赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
  • 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值

日期类的实现

  • 成员函数后面加上const以后,普通对象和const对象都可以调用
  • 只要成员函数内部不修改成员变量,就应该加上const
  • 流插入不能写成成员函数
  • Date对象默认占用第一个参数,即左操作数
    在这里插入图片描述
    在这里插入图片描述
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
	friend ostream& operator<<(ostream& out, const Date& d);//友元函数的声明
	friend istream& operator>>(istream& in, Date& d);
public:
	Date(int year = 1, int month = 2, int day = 3);
	void Print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
	Date(const Date& d)
	{
		_year = d._year;//this->_year=d._year(隐藏的参数this指针)
		_month = d._month;
		_day = d._day;
	}
	bool operator<(const Date& x)const;
	bool operator==(const Date& x)const;
	bool operator<=(const Date& x)const;
	bool operator>(const Date& x)const;
	bool operator>=(const Date& x)const;
	bool operator!=(const Date& x)const;
	int GetMonthDay(int year, int month);

	
	Date& operator+=(int day);
	Date operator+(int day)const;
	Date& operator-=(int day);
	Date operator-(int day)const;

	Date& operator++();//前置++
	Date operator++(int);//后置++(增加这个int参数,不是为了接受具体值,仅仅是为了占位,与前置++构成重载
	Date& operator--();
	Date operator--(int);


	int operator-(const Date& d) ;
private:
	int _year;
	int _month;
	int _day;
};

// 流插入不能写成成员函数?
// 因为Date对象默认占用第一个参数,就是做了左操作数
// 写出来就一定是下面这样子,不符合使用习惯
//d1 << cout; // d1.operator<<(cout); 

ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
#include"project3.h"
Date::Date(int year , int month , int day )
{
	if (month > 0 && month < 13 && day>0 && day < GetMonthDay(year, month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "非法日期" << endl;
		assert(false);
	}
}
int Date::GetMonthDay(int year, int month)
{
	static int daysarr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		return 29;
	}
	return daysarr[month];
}
bool Date::operator<(const Date& x)const
{
	if (_year < x._year)
	{
		return true;
	}
	if (_year == x._year && _month < x._month)
	{
		return true;
	}
	if (_year == x._year && _month == x._month && _day < x._day)
	{
		return true;
	}
	return false;
}
bool Date::operator==(const Date& x)const
{
	if (_year == x._year && _month == x._month && _day == x._day)
	{
		return true;
	}
	return false;
}
bool Date::operator<=(const Date& x)const
{
	return (*this < x || *this == x);
}
bool Date::operator>(const Date& x)const
{
	return !(*this <= x);
}
bool Date::operator>=(const Date& x)const
{
	return !(*this < x);
}



bool Date::operator!=(const Date& x)const
{
	return !(*this == x);
}


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

Date Date::operator+(int day)const
{
	Date tmp (*this);
	tmp._day += day;
	while (tmp._day > GetMonthDay(_year, _month))
	{
		tmp._day -= GetMonthDay(_year, _month);
		++tmp._month;
		if (tmp._month == 13)
		{
			tmp._month = 1;
			++tmp._year;
		}
	}
	return tmp;
}

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

Date& Date::operator++()//前置++
{
	*this += 1;
	return *this;
}

Date Date::operator++(int)//增加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;
}

int Date::operator-(const Date& d) 
{
	Date min = *this;
	Date max = d;
	int flag = 1;
	if (min > max)
	{
		min = d;
		max = *this;
		flag = -1;
	}
	int n = 0;
	while (min != max)
	{
		min++;
		n++;
	}
	return n * flag;
}

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	int year, month, day;
	in >> year >> month >> day;
	if (month > 0 && month < 13 && day>0 && day < d.GetMonthDay(year, month))
	{
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
		cout << "非法日期" << endl;
		assert(false);
	}
	return in;
}
#include"project3.h"
int main()
{
	Date d1(2022, 2, 22);
	d1 += 100;//日期类加等
	d1.Print();
	d1 -= 200;//日期类减等
	d1.Print();
	++d1;//前置++
	d1.Print();
	d1++;//后置++
	d1.Print();

	Date d2(2025, 2, 26);
	Date d3(d2 + 100);
	//Date d3=d2+100;
	Date d4 = d2;//用已经存在的一个对象初始化另一个对象————构造函数,不是赋值
	d4.Print();

	d2.Print();
	cout << d1 - d2 << endl;//日期计算天数差
	
	cout << d2 << d3 << d1;//流插入
	//流提取
	cin >> d1 >> d2;
	cout << d2 << d1;
	return 0;
}

链接: C++类和对象基本认识下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值