C++深入了解类和对象

C++带你深入了解类和对象(超细节)

在这里插入图片描述

1. 引言

class Date()   {}

​ 一个类,如果没有任何成员,则称为空类。而当一个类什么都不创建时,里面真的什么都没有吗? 并不是!

​ 编译器会自动生成六个类默认的成员函数:

1.构造函数

2.析构函数

3.拷贝构造函数

4.运算符重载函数

5.const成员函数

6.取地址及const取地址操作符重载函数

在这里插入图片描述

​ 这六个默认函数都遵守的规则是:如果类中主动写有自定义的默认成员函数,则采取手动编写的,否则采取编译器默认生成的。

​ 本篇就以Date日期类举例,重点介绍六位天选之子的默认成员函数。

2.构造函数

2.1 介绍与初始化

​ 这里有Date类,对于Date类:

class Date()
{
 public:
    void Init(int year,int month,int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    
    
 private:
    int _year = 2023;//年;可以在声明位置给上初始值,但并非初始化,而是给缺省值
    int _month;//月
    int _day;//日
    
};

int main()
{
    Date x;
    x.Init(2022,3,4);
    
    Date y;
    x,Init(2023,3,4)
    
    return 0;
}

​ 在这里创建了Init()函数进行了初始化,但是如果每次调用对象都需要用Init来调用未免有些不方便,所以C++提供了构造函数

构造函数名称与类名相同,创建类对象由编译器自动生成,每个成员的初始值在对象的整个生命周期只有一次。

class Date()
{

public:
    void Date(int year,int month,int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
 private:
    int _year;//年
    int _month;//月
    int _day;//日

};

注意: 这里类内的构造函数成员只是进行了声明并不是初始化,没有开辟内存空间 ,只有当对象调用了之后才会开辟空间。

2.2 另一种初始化玩法:初始化列表初始化

​ 除了上述最基本初始化方法初始化,还有构造函数初始化的另一种玩法:初始化列表。这种初始化具有一定的意义,即当类中有三种:

1.const 类型

2.引用类(如:int&)

3.自定义类

​ 当一个类中有以上三种成员时,必须使用初始化列表初始化! 而不能用一般的初始化方法。具体例子如下:

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

​ 以上初始化列表的格式需要注意以下问题:

​ 1.初始化列表写在具体实现前(应在{…}上面)

​ 2.冒号开始,逗号封装

当了解初始化列表以后,我们需要明白两件事:

1.以后尽量使用初始化列表初始化,因为无论你使不使用,对于自定义类型成员,一定会先使用初始化列表初始化(编译器默认)。

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


3.析构函数

3.1 介绍

​ 通过构造函数我们知道一个对象是怎么来的,那么一个对象又是怎么没的呢?是通过析构函数

​ 析构函数不是完成对象的销毁工作的,销毁工作由编译器完成。对象被销毁时会自动调用析构函数,

完成资源清理的工作

3.2 特性

​ 1.析构函数名与类型相同,不过在类名前加上~。

​ 2.无参数无返回值。

​ 3.一个类只能由一个析构函数。

​ 4.对象生命周期结束时,C++自动调用析构函数。

~Date()
{
    cout << "调用析构函数" << endl;
    ...
}

​ 如果类中没有申请资源时,比如Date类,就可以直接使用编译器生成的默认析构函数。但是如果动态开辟了空间占用了内存,则必须手写析构函数释放掉空间,否则会造成资源泄露,比如当使用malloc动态开辟了内存的类。


4. 拷贝构造函数

4.1 介绍

​ 在生活中,长得一摸一样的人我们称之为双胞胎。在创建对象时我们也可以通过函数创建一个一样的对象。

在这里插入图片描述

​ 那么,在什么情况下会使用拷贝构造函数呢?

● 使用已存在的对象创建对象

● 函数参数类型为类类型对象

● 函数返回值为类类型对象

4.2 特征

​ 拷贝构造函数特征如下:

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

​ 2.拷贝构造函数的参数==只有一个且必须是类类型对象的引用==(一般还用const修饰),如 :const Date& d。使用值传递的方式(const Date d)会引 发无限递归编译器直接报错。

​ 3.拷贝构造函数在用已存在的类类型对象创建时由编译器自动调用。

​ 4.未手动定义,编译器默认生成拷贝构造函数

​ 具体使用方式如下:

class Date()
{
  public:
    Date(const Date& d)  //拷贝构造函数  d为要构建的一摸一样的对象
	{  
       _year = d._year;
        _month = d._month;
        _day = d._day;
	}
  private:
    int _year;
    int _month;
    int _year;
};


注意:

​ 1.拷贝构造函数内,内置类型如:int、double等使用时是按字节方式直接进行浅拷贝(值拷贝)的,而自定义类型是调用其拷贝构造函数完成拷贝的。

​ 2.类中无资源动态申请的拷贝构造函数是否写都可以,但是一旦涉及动态申请了,就必须手写拷贝构造函数,否则将一直保持浅拷贝状态

5. 运算符重载函数

5.1运算符重载

5.1.1 介绍

​ 假如有两个对象d1、d2,在Date类中想要比较两个对象的日期有很多种方法,但是有些方法很麻烦。所以想法大胆一点,能不能直接使用d1>d2的方法直接比较? 可以! 只要使用运算符重载 ,就相当于赋给了运算符新的使用方法

​ C++为了增加代码的可读性引入了运算符重载,运算符重载是特殊的函数,也具有返回值、名字和参数。

5.1.2 特性

​ 函数名:operator + 重载运算符号

​ 函数原型:返回值类型 operator +操作符(参数列表)

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

bool operator >(const Date& d)
	{
		if (_year < d._year)
			return false;
		else if (_month < d._month)
			return false;
		else if (_day < d._day)
			return false;
		else
			return true;

	}


在使用中有以下几点需要注意:

​ ● ( . : : sizeof ?: .* ) 以上五个运算符不能重载

​ ● 不能通过链接其他符号来创建新的操作符。如operator @ 。

​ ● 用于内置计算的运算符,其含义不能改变。如内置+。

​ ● 作为类成员函数重载时,其形参看起来比操作数数目少1,因为还有一个隐藏的指针this。加上this指针,参数超过三个及以上就会报错。

​ ● 重载函数必须有一个类的类型参数(如:Date)。

5.2 赋值运算符(operator=)重载

5.2.1 介绍

​ 默认重载的赋值运算符功能很简单,就是将原有对象的所有成员变量一一赋值给新对象,这和默认拷贝构造函数的功能类似。 对于简单的类,默认的赋值运算符一般就够用了,我们也没有必要再显式地重载它。 但是当类持有其它资源时,例如动态分配的内存、打开的文件、指向其他数据的 指针 、网络连接等,默认的赋值运算符就不能处理了,我们必须显式地重载它,这样才能将原有对象的所有数据都赋值给新对象。

5.2.2 特性

​ ● 参数类型: const C&(C为类名)

​ ● 返回值类型:C&

​ ● 返回*this指针

class Date()
{
    public:
    		Date(int year = 1900, int month = 1, int day = 1)   //构造函数
{
				_year = year;
				_month = month;
				_day = day;
                
                Date(const Date& d)    //拷贝构造函数
                {
                  _year = year;
				_month = month;
				_day = day;
                }
                
                
               Date& operator = (const Date& d)   //operator=的赋值运算符重载
               {
                   	if(this != &d)

					_year = d._year;
					_month = d._month;
					_day = d._day;
					return *this;
                   
                   
                   
               }
                
                
}
    
    
  private:
   int _year;
   int _month;
   int _day;
 
    
    
    
}

● 注意: 赋值运算符只能重载成员函数不能重载全局函数


6.const成员函数

6.1 介绍

​ 将const修饰的成员函数称为const成员函数,本质上是修饰this指针,表明在该成员函数中不能对任何的成员进行修改

在这里插入图片描述

class Date()
{
 public:
    Date(int year,int month,int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void Print() const		//const写在后
	{
			cout << "Print()const" << endl;
			cout << "year:" << _year << endl;
			cout << "month:" << _month << endl;
			cout << "day:" << _day << endl << endl;
	}
    
 private:
    int _year = 2023;//年;可以在声明位置给上初始值,但并非初始化,而是给缺省值
    int _month;//月
    int _day;//日
    
};

int main()
{
    Date x;
    x.Init(2022,3,4);
    
    Date y;
    x,Init(2023,3,4)
    
    return 0;
}

● 注意 :**const成员无法调用非const函数 ,而非const成员可以调用const函数。**不过这样不利于封装,这样一来就可以在外部调用到const成员函数调用到private的内部私有成员。

7.取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

class Date
{
public :
		Date* operator&()
		{
				return this ;
		}
		const Date* operator&()const
		{
				return this ;
		}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需
要重载,比如想让别人获取到指定的内容!
int main()
{
Date x;
x.Init(2022,3,4);

Date y;
x,Init(2023,3,4)

return 0;

}




> ●  注意 :**const成员无法调用非const函数 ,而非const成员可以调用const函数。**不过这样不利于封装,这样一来就可以在外部调用到const成员函数调用到private的内部私有成员。



## 7.取地址及const取地址操作符重载



这两个默认成员函数一般不用重新定义 ,编译器默认会生成。  

```c++
class Date
{
public :
		Date* operator&()
		{
				return this ;
		}
		const Date* operator&()const
		{
				return this ;
		}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需
要重载,比如想让别人获取到指定的内容!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Arthur___Cui

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

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

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

打赏作者

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

抵扣说明:

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

余额充值