C++类与对象(中)

大家好鸭

上期我们谈论了一些关于C++中类的知识,初步介绍一些类的使用方法

上期文章

本篇文章,我们进一步来谈谈类的其他细节

类的6个默认成员函数

我们先来看一个类

class A
{
};

看着是一个空类,但即使是一个空类,编译器也会生成6个默认成员函数,它们分别是构造函数析构函数拷贝构造赋值重载,还有两个普通对象和const对象的取地址重载

构造函数

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

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

默认构造函数分为两种情况,一种是构造函数没有参数,一种是构造函数全缺省,以上两种情况编译器都视为默认构造函数。

当我们显示写好了之后编译器便不会自动生成默认构造函数

注意:一个类只能有一个默认构造函数

上面一段代码定义了一个日期类Date,有一个与类名相同的函数,那个就是构造函数

构造函数的主要功能就是:在对象实例化时初始化对象

并不是创建对象

构造函数还有以下特点

  1. 函数名与类名相同
  2. 无返回值
  3. 在实例化时自动调用
  4. 构造函数可以重载
  5. 没有显示定义时自动生成,定义了就不生成
  6. 编译器生成的默认构造,对内置类型不处理,对自定义类型调用对应的构造函数
class Date
{
private:
 // 基本类型(内置类型)
 int _year = ;
 int _month ;
 int _day ;
//自定义类型
 Time A;
};

有可能读者会认为编译器生成的默认构造函数没什么用

当一个类中只有自定义类型时,就可以看出编译器生成的默认构造的优势了

自定义类型调用对应构造,这样这个类就可以用默认生成的就好了,不用直接写构造函数

这样的前提是自定义类型都已经写好自己的构造函数。

细心的读者可能注意到了

构造函数我使用public修饰它,这是为了构造函数可以在类外访问它,可以在类外实例化对象,

当修饰词改成private时,就不能在类外创建对象了

析构函数

 析构函数与构造函数恰恰相反,构造函数负责初始化,析构函数负责销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。 

	~Date()
	{
	}

像这样便是Date类的析构函数,析构函数和构造函数一样,对内置类型不处理,对自定义类型

调用对应的析构函数

写析构函数主要是针对动态申请的空间需要释放

析构函数有如下特点

  1. 析构函数名是在类名前加上~构成
  2. 无参数无返回值
  3. 一个类只有一个析构函数,不可重载,没有显示定义的话,编译器自动生成
  4. 对象声明周期结束时,编译器自动调用析构函数

拷贝构造函数

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

简单的讲就是,用一个已知的对象去初始化一个新创建的对象

class Date
{
public:
	Date(const Date& d)//拷贝构造
	{
		_day = d._day;
		_month = d._month;
		_year = d._year;
	}
	Date(int year,int month,int day)
	{
		_day = day;
		_month = month;
		_year = year;
	}
	~Date()
	{
	}
private:
	int _day;
	int _month;
	int _year;
};

使用方法如下

int main()
{
	Date A(2000, 1, 1);
	Date B(A);
}

需要注意的是:

在拷贝构造的形参必须使用引用传参,否则会引发无穷递归,导致程序崩溃

编译器默认生成的拷贝构造函数只是简单的值拷贝,当有动态开辟的空间时,可能会导致该空间被两次析构,引发程序崩溃

像上图那样,简单的值拷贝会引发这样的问题:两个指针指向同一块空间,当原来的对象析构时将动态开辟的空间释放后,拷贝出来的对象又将空间释放一遍就会报错,这样的情况,拷贝构造就要写深拷贝,为int*b也开辟一块同样大小的空间

拷贝构造有以下特点

  1. 拷贝构造是构造函数的一个重载形式
  2. 参数只有一个,且为引用
  3. 编译器自动生成的拷贝构造函数是只进行值拷贝的浅拷贝,自定义类型是调用对应的拷贝构造
  4. 在将类做为参数的情况下,会调用拷贝构造构造一个临时对象,再进行传参,所以能采用引用返回的时候尽量采用引用返回

赋值运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
简单的讲:运算符重载是为了简单易懂的操作自定义类型
函数原型:返回值类型 operator操作符(参数列表)
Date& Date::operator++()
{
	_day++;
	if (_day > GetMonthDay(_year, _month))
	{
		_day = 1;
		_month++;
	}
	if (_month >= 13)
	{
		_month =1;
		_year++;
	}
	return *this;
}

像这样就重载了++运算符,自定义了++运算符在自定义类型Date中的使用

前置++和后置++有特殊的地方

Date& Date::operator++()//前置
{
	_day++;
	if (_day > GetMonthDay(_year, _month))
	{
		_day = 1;
		_month++;
	}
	if (_month >= 13)
	{
		_month =1;
		_year++;
	}
	return *this;
}

Date Date::operator++(int)//后置
{
	Date tmp(*this);
	_day++;
	if (_day > GetMonthDay(_year, _month))
	{
		_day = 1;
		_month++;
	}
	if (_month >= 13)
	{
		_month = 1;
		_year++;
	}
	return tmp;
}

后置++在参数的地方多了一个int参数用于区分

操作符重载有以下要注意的地方
  1. 不能创建新操作符
  2. 重载操作符必须有一个类类型操作符
  3. 内置类型的操作符不可改变其含义
  4. 当类成员重载时,形参比操作数少一个,因为有一个隐藏的this指针
  5. 有5个操作符不可重载分别是   .*   ::   sizeof   ?:   .      
  6. 注意是否可以用引用返回
  7. 赋值运算符只能重载成成员函数,不能重载成全局函数,因为编译器会生成一个默认的重载运算符,不在类中显示定义的话,会与编译器生成的冲突
  8. 编译器生成的赋值运算符也是简单的值拷贝

const成员

	void ab()const
	{

	}

像这样将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

const Date A(2000, 1, 1);

像这样是const对象

const对象有它的特殊之处,如const Date A,对这样一个类取地址,结果是 const Date*

当const对象作为形参时,会将const Date*传递给函数中的this指针

但普通函数的this指针类型是Date*,这样就出现了类型不兼容的问题

解决办法是将成员函数重载一份const成员函数,当返回值也不想被外界修改时,

可以给返回值也加上const,于是我们会见到这样的代码

const char& operator[](size_t pos) const

取地址和const取地址操作符

这两种操作符一般不用重新定义

本篇到此结束,谢谢大家,若有错误,还请不吝赐教

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值