c++类和对象

本文详细介绍了C++中的类和对象,包括类的基础概念、定义、封装、作用域与实例化,以及类的大小和this指针。重点讨论了构造函数、析构函数、拷贝构造函数、赋值运算符重载等重要成员函数,并提到了explicit关键字、static成员和友元函数的概念。此外,文章通过日期类的实现进一步阐述了类的使用和相关操作。
摘要由CSDN通过智能技术生成

作者: sleepygod
日期: 4-16
也许 不负光阴就是最好的努力 而努力就是最好的自己

博客照片

c++类和对象

1.类和对象基础概念

1.类的引入

C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如: 之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现, 会发现struct中也可以定义函数。在C++中更喜欢用class来代替。

下面构建一个日期类

class Date
{
public:
	Date(int year, int month, int day);
	Date(const Date& d);
	
private:
	int _year;
	int _month;
	int _day;

};

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

2.类的定义

class className
{
// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。

类的两种声明方式:

第一种声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内 联函数处理。

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
private:
	int _year;
	int _month;
	int _day;

};

第二种类声明与成员函数分离,注意:成员函数名前需要加类名::

class Date
{
public:
	Date(int year, int month, int day);
	
	Date(const Date& d);
	
private:
	int _year;
	int _month;
	int _day;

};

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

一般情况下,更倾向于第二种写法.第二种写法当后期维护查看代码时,会比较轻松.

3.类的封装

1.访问限定符

有public, protected,private三类

public修饰的成员在类外可以直接被访问.

protected,private修饰的成员在类外不可以直接被访问.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzB5wXsr-1681652973895)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20221028222032105.png)]

class的默认访问权限为private,struct为public.

面向对象的三大特征:封装,继承,多态(当然不止这几个特征,因为这三个最出名).

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来 和对象进行交互.

4.类的作用域与实例化

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域.

class Date
{
public:
	Date(int year, int month, int day);
	
	Date(const Date& d);
	
private:
	int _year;
	int _month;
	int _day;

};

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

用类类型创建对象的过程,称为类的实例化

如上面 Date A;即为实例化.

5.类的大小

类的存储方式:只保存成员变量,成员函数存放在公共的代码段.

所以类的大小,实际就是该类的中"成员变量"之和,与结构体的大小计算相同,需要注意默认对齐数.

当类为空类时,编译器会给空类分配一个字节来唯一标识这个类.

6.this指针

this指针类型: 类类型*const 即在函数中不能给this赋值,只能在成员函数中使用.

this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针.

this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递

class Date
{
public:
	Date(int year, int month, int day);
	
	Date(const Date& d);
	void Print()
	{
		cout << _year;
	}
	
private:
	int _year;
	int _month;
	int _day;

};

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}





int main()
{

	Date a(2001, 10, 20);
	a.Print();
	return 0;
}

对于上述代码解释 当调用成员函数Print()时,实际上是Print(Date *this);所以输出语句是 cout<<this->_year;

但是在一般情况下都不写,来简化代码. 注:this指针可以指向空.且this指针存储在栈区

2.类和对象重要函数

6个默认成员函数

image-20230416103854932

1.构造函数

example:

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。即替代了我们对对象初始化的过程.

特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任 务并不是开空间创建对象,而是初始化对象。

特征如下:

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
  5. 如果类中没有显示定义构造函数,则c++编译器会自动生成一个无参的默认构造.此构造函数对内置类型不做处理,对自定义类型才会做处理
2.析构函数

日期类中不需要写析构函数,所以此处不在展示样例

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

特性如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
  5. 如果类中没有显示定义析构函数,则c++编译器会自动生成一个析构函数.
3.拷贝构造函数

example:

Date::Date(const Date& d)
{
	if (&d == nullptr)
	{
		*this=Date();
	}
	else
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
	
}

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用。

特性如下:

  1. 拷贝构造函数是构造函数的一个重载形式。

  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。

  3. 若未显式定义,编译器会生成默认的拷贝构造函数。此拷贝构造是浅拷贝,对于属性是内置类型的类适用,对于自定义类型,则可能会产生一些错误,在后续过程中会讲解到.

4.赋值运算符重载

example:

Date& Date::operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
	return *this;
}

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。其中.* :: sizeof ?: . 五个运算符不能被重载. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝和默认拷贝构造类似.

5.普通对象取地址重载

example

Date* Date::operator&()
{
	return nullptr;

}

无法得到对象的地址

使用不多,就不做讲解

6.const对象取地址重载

example:

const Date* Date::operator&()const
{
	return nullptr;
}

无法得到对象的地址

使用不多,就不做讲解

3.类和对象升级讲解

1.构造函数的其他形式

构造函数的函数体通过赋值的方式来给数据成员指定初始值。也就是说构造函的函数体是采用先定义后赋值的方式来做。但是这种方式会产生一些问题,比如const类型数据,引用类型数据,必须在定义时进行赋值.

image-20230416194121726

所以,我们没有办法对于这些类型数据赋值吗?当然不是,现在将要提出初始化列表

初始化列表使用初始化的方式来为数据成员指定初始值。也就是说成员初始化列表是在数据成员定义的同时赋初值。

Date::Date(int year, int month, int day)
	:_year(year)
	,_month(month)
	,_day(day)
{}

这是就可以解决const,引用类型的数据,自定义类型成员(且该类没有默认构造函数时).并且尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化。

同时员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关

Date::Date(int year, int month, int day)
	:_day(day)
	,_month(month)
	, _year(_day)
{}

image-20230416194845894

2.explicit关键字

造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。

image-20230416195128191

用explicit修饰构造函数,将会禁止构造函数的隐式转换

image-20230416195401952

3.static成员

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化

静态成员使用

#pragma once

#include<iostream>
using namespace std;

class MyClass
{
public:
	MyClass();
	static int get_a() { return _a; }

private:
	static int _a;
};
int MyClass::_a = 0;
MyClass::MyClass()
{
	_a++;
}

int main()
{
	MyClass a[50];
	cout<< MyClass::get_a() << endl;
	
	return 0;
}

结果:50

4.友元函数

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在 类的内部声明,声明时需要加friend关键字。并且不能用const修饰,不受访问限定符控制.使用方式如下:

lass Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
	friend void swap(Date& d1, Date& d2);
...



}

4.日期类实现

//h
#pragma once
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
	friend void swap(Date& d1, Date& d2);

public:
	int getMonthDay();
	Date(int year=2001, int month=1, int day=1);
	Date(const Date& d);
	Date& operator=(const Date& d);
	Date& operator-=(int day);
	Date& operator+=(int day);
	Date operator-(int day)const;
	Date operator+(int day)const;
	Date& operator++();
	Date operator++(int);
	Date& operator--();
	Date operator--(int);
	int operator-(const Date& d)const;

	
	
	bool operator==(const Date& d)const;
	bool operator!=(const Date& d)const;
	bool operator>(const Date& d)const;
	bool operator>=(const Date& d)const;
	bool operator<(const Date& d)const;
	bool operator<=(const Date& d)const;


	
	void display();


	private:
		int _year;
		int _month;
		int _day;
		
};


void swap(Date& d1, Date& d2);

ostream& operator<<(ostream& _cout, const Date& d);

istream& operator>>(istream& _cin, Date& d);

//cpp
#include"Date.h"
int Date::getMonthDay()
{
	vector <int> month = { 31,28,31,30,31,30,31,31,30,31,30,31 };
	/*if (_year % 400 == 0 || (_year % 4 == 0 && _year % 100 != 0))
	{
		month[1] = 29;
	}*/
	return month[_month - 1];
}
Date::Date(int year, int month, int day)
	:_year(year)
	,_month(month)
	,_day(day)
{}
Date::Date(const Date& d)
{
	
		_year = d._year;
		_month = d._month;
		_day = d._day;
	
	
}
Date& Date::operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
	return *this;
}


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

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



Date Date::operator-(int day)const
{
	Date tmp(*this);
	return tmp -= day;
}
Date Date::operator+(int day)const
{
	Date tmp(*this);
	return tmp += day;
}




Date& Date::operator++()
{
	return *this += 1;
}
Date Date::operator++(int)
{
	Date tmp(*this);
	*this += 1;
	return tmp ;
}
Date& Date::operator--()
{
	return *this -= 1;
}
Date Date::operator--(int)
{
	Date tmp(*this);
	*this -= 1;
	return tmp;
}



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

}


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

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

bool Date::operator>(const Date& d)const
{
	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)const
{
	return (*this==d) ||(*this > d)  ;
}

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

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

void Date::display()
{
	 cout<<_year<<"-"<< _month << "-" << _day << endl;
}


ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day<<" ";
	return _cout;
}


istream& operator>>(istream& _cin, Date& d)
{
	_cin >> d._year;
	_cin >> d._month;
	_cin >> d._day;
	return _cin;
}

void swap(Date& d1, Date& d2)
{
	std::swap(d1._year, d2._year);
	std::swap(d1._month, d2._month);
	std::swap(d1._day, d2._day);
}
//cpp
#include"Date.h"
int main()
{
	Date d1(2022);
	
	(d1 + 400).display();
	
	
	Date d2(d1);
	Date d3;
	d3 = d1;
	d1 += 400;
	
	cout << d1  << d2 << d3<<endl;
	
	d1.display();
	d2.display();
	d3.display();
	cout << (d1 < d2);
	cout << (d1 - d2)<<endl;
	cout << (d2 - d1);
	
	return 0;
}

image-20230416212821273

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sleepymonstergod

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

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

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

打赏作者

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

抵扣说明:

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

余额充值