首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C++新旅程:类的拷贝构造函数 、赋值运算符重载 和const成员函数

C++新旅程:类的拷贝构造函数 、赋值运算符重载 和const成员函数

原创
作者头像
池央
发布2024-11-23 06:41:54
发布2024-11-23 06:41:54
5810
举报
文章被收录于专栏:好事连连好事连连

好事发生

Java面试宝典:MongoDB实战技巧 作者:忆遂愿

https://cloud.tencent.com/developer/article/2466159?shareByChannel=link

文章对 MongoDB 知识的全面阐述,质量很高。从基本概念、Java 驱动使用、数据操作、安全性能问题与解决、数据一致性事务处理,到数据模型设计、技术集成和存储图片优势等方面讲解详细、条理清晰,体现出作者深入的理解。

1. 拷贝构造函数

1.1概念

拷贝构造:用同类型的已经存在的对象,拷贝给另一个要创建初始化的对象。

注意:在编译器中生成的默认拷贝构造中,内置类型按字节直接拷贝(浅拷贝),自定义类型调用其的拷贝构造函数完成(深拷贝)

补充:

  • 浅拷贝:按内存存储按字节序完成拷贝,也叫做值拷贝。
  • 深拷贝:按形状拷贝,开一样大的空间。

1.2特征

(1)拷贝构造函数是构造函数的一个重载形式

(2)参数有且只有一个,必是类类型对象的引用,使用传值编译器会直接报错(因为会引发无穷递归调用

1.3拷贝构造函数典型调用场景

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

简单代码示例一

代码语言:C++
复制
#include<iostream>
using namespace std;
class Date
{
public:
    //构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //拷贝构造函数
	Date(const Date& d) //Date(const Date d) //若传值,编译器报错,会引发无穷递归
	{
		_year = d._year;//等价于this->_year=d._year
		_month = d._month;
		_day = d._day;
	}
	void PrintDate()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.PrintDate();
	Date d2(d1);//等价于Date d2=d1;
	d2.PrintDate();
	Date d3 = d1;
	d3.PrintDate();
	return 0;
}

1.4总结

(1)无管理资源/类中都是自定义类型成员变量,内置类型成员变量没有指向资源,默认生成拷贝构造即可。如Date类 ,MyQueue类等

(2)一般来说不需要显示写析构函数,就无需显示写拷贝构造函数

(3)内部有指针/一些值指向资源,就需要显示写析构函数释放,通常就需要显示写拷贝构造函数完成深拷贝。如Stack类,Queue类,List类等

2. 赋值运算符重载

在了解赋值重载函数之前,我们需要了解什么是运算符重载,这里做一个简单的介绍。

C++为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数,也具有其

返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

2.0运算符重载函数

(1)函数名:operator+需要重载的运算符符号

(2)函数原型:返回值类型 operator+符号(形参参数列表)

(3)必须有一个类类型的参数

(4)简单代码示例

2.1概念

赋值重载:一个已经存在的对象,拷贝复制给另一个已经存在的对象(要区别拷贝构造)

注意: 1、作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。2、赋值运算符只能重载成类的成员函数不能重载成全局函数。原因如下:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。3、 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符 重载完成赋值。注:如果类中未涉及到资源管理,赋值运算符一般不用显示实现;一旦涉及到资源管理则必 须要实现。

2.2赋值运算符重载格式

  • 返回值类型:类名&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 参数类型:const 类名&(传引用提高效率)
  • 返回*this(要符合连续赋值的含义)
  • 检测是否自己给自己赋值

代码示例

代码语言:C++
复制
#include<iostream>
using namespace std;
class Date
{
public:
	//构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造函数
	Date(const Date& d) 
	{
		_year = d._year;//等价于this->_year=d._year
		_month = d._month;
		_day = d._day;
	}
	//赋值重载函数
	Date& operator=(const Date& d)
	{
		if (this != &d)//检测是否自己给自己赋值
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	void PrintDate()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.PrintDate();
	Date d2 = d1;//拷贝构造
	d2.PrintDate();
	Date d3(2024, 5, 20);
	d1 = d3;//赋值重载
	d1.PrintDate();
	return 0;
}

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实

现吗?当然像日期类这样的类是没必要的。那么下面的类呢?我们一起验证一下

代码示例二

代码语言:C++
复制
#include<iostream>
using namespace std;
class Time
{
public:
    Time(int hour = 0, int minute = 0, int second = 0) 
    {
        _hour = hour;
        _minute = minute;
        _second = second;
    }
    void PrintTime()
    {
        cout << _hour << "/" << _minute << "/" << _second << endl;
    }
private:
    int _hour;
    int _minute;
    int _second;
};

class Date
{
public:
    Date(int year = 1970, int month = 1, int day = 1, const Time& t = Time()) 
        : _year(year), _month(month), _day(day), _t(t) // 使用初始化列表
    {
    }

    void PrintDate()
    {
        cout << _year << "/" << _month << "/" << _day << "/";
       _t.PrintTime();
    }

private:
//内置类型
    int _year;
    int _month;
    int _day;
//自定义类型
    Time _t;
};

int main()
{
    // 使用默认Time对象初始化Date对象
    Date d1(2024, 11, 11);
    d1.PrintDate();

    // 使用自定义的Time对象初始化Date对象
    Time t(12, 15, 15);
    Date d2(2024, 10, 10, t);
    d2.PrintDate();

    //赋值重载
    d2=d1;
    d2.PrintDate();
    return 0;
} 

##2.3总结

出了作用域,返回对象未析构可用引用返回,减少拷贝

  • 返回对象生命周期到了会析构,传值返回(会生成临时对象)
  • 返回对象生命周期没到不会析构,传引用返回

3. const成员函数

将const修饰的“成员函数”称之为const成员函数。

const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员变量进行修改。

代码示例

代码语言:C++
复制
#include<iostream>
using namespace std;
class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	//构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void PrintDate()
	{
		cout << "PrintDate()" << endl;
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	void PrintDate()const
	{
		cout << "PrintDate()const" << endl;
		cout << _year << "/" << _month << "/" << _day << endl;
	}
};
int main()
{
	Date d1;
	d1.PrintDate();
	const Date d2(2024, 5, 20);
    d2.PrintDate();
	return 0;
}

运行结果:

再来看看下图一

图二

图三

图四

通过调试我们可以知道const对象可以调用const成员函数;非const对象可以调用非const成员函数。这属于权限的平移

  • const对象可以调用非const成员函数吗?可以,权限的缩小
  • 非const对象可以调用const成员函数吗?不可以,权限的放大
  • const成员函数内可以调用其它的非const成员函数吗?可以,权限的缩小
  • 非const成员函数内可以调用其它的const成员函数吗?不可以,权限的放大

一句话总结权限不能被放大!!!

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

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

简单示例:

代码语言:C++
复制
class Date
{ 
public :
 Date* operator&()
 {
 return this ;
}
 const Date* operator&()const
 {
 return this ;
 }
private :
 int _year ;
 int _month ; 
 int _day ; 
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需

要重载,比如想让别人获取到指定的内容!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 好事发生
  • 1. 拷贝构造函数
    • 1.1概念
    • 1.2特征
    • 1.3拷贝构造函数典型调用场景
    • 1.4总结
  • 2. 赋值运算符重载
    • 2.0运算符重载函数
    • 2.1概念
    • 2.2赋值运算符重载格式
  • 3. const成员函数
  • 4. 取地址及const取地址操作符重载
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档