IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C++】拷贝构造函数和运算符重载 -> 正文阅读

[C++知识库]【C++】拷贝构造函数和运算符重载

在这里插入图片描述


1.拷贝构造的补充

编译器默认生成的拷贝构造:默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝称为浅拷贝。

  • 不写拷贝构造,编译器会自动生成默认拷贝构造,拷贝方式为浅拷贝

  • 默认构造函数:对于内置类型成员值拷贝,浅拷贝:按照字节序传值。

  • 默认构造函数:对于自定义类型,去调用自定义类型的拷贝构造

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;

		_a = (int*)malloc(sizeof(int)*capacity);
		assert(_a);
		_top = 0;
		_capacity = capacity;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

比如上述程序会出现内存问题。Stack内部没有定义拷贝函数,所以编译器会自动生成拷贝构造函数,拷贝方式为浅拷贝。由于Stack成员变量为指针,按照内存序拷贝拷贝的是指针,因此st1和st2指向的是同一个位置。同一块内存空间析构两次所以会出现内存问题。

image-20220517003406740

image-20220517003638525

浅拷贝:

  • 1.指向同一块空间,修改数据会相互影响
  • 2.这块空间析构时会释放两次,程序会崩溃。
  • 解决方案:实现深拷贝

程序2

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;
		_top = 0;
		_capacity = capacity;
        //memset是按照一个字节一个字节进行赋值
        //所以初始化最好每个字节初始化为0
		memset(_a, 0, sizeof(int) * 10);
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		_capacity = _top = 0;
	}
private:
	int _a[10];
	int _top;
	int _capacity;
};
int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

image-20220517004447571

如果存放的是数组,可以正常的拷贝。因为st1存储的数组和st2存储的数组指向的不是同一块空间。

image-20220517005422149

1.1自定义类型的拷贝构造

自定义类型:去调用自定义类型的拷贝构造

程序一

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;
		_a = (int*)malloc(sizeof(int)*capacity);
		assert(_a);
		_top = 0;
		_capacity = capacity;
	}
    Stack(Stack& st)
    {
        cout<<"调用Stack的拷贝构造"<<endl;
    }
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
class MyQueue{
    private:
    	int _size=0;
    	Stack _st1;
    	Stack _st2;
}
int main()
{
    MyQueue mq1;
    MyQueue mq2(mq1);
    return 0;
}

MyQueue既有自定义类型又有内置类型。对于内置类型,编译器会继续浅拷贝,对于自定义类型调用自定义类型的拷贝构造

1.2总结

1.用户不写拷贝构造编译器会默认生成一个拷贝构造

2.自定义类型成员,去调用这个成员的拷贝构造

3.一般类,自己生成的拷贝构造就够用了。只有像Stack类这种需要自己直接管理资源需要实现深拷贝。

4.对于在堆区上开辟了空间的类,就需要使用深拷贝。

2.运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

2.1特征

  • 1.不能通过连接其他符号来创建新的操作符:比如operator@

  • 2.重载操作符必须有一个类类型或者枚举类型的操作数

  • 3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义

  • 4.作为类成员的重载函数时,其形参看起来比操作数数目少1

    成员函数的操作符有一个默认的形参this,限定为第一个形参

    1. *. 、:: 、sizeof 、?: 、. **注意以上5个运算符不能重载。

    .* 域作用符,sizeof ,三目操作符 .操作符

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
//如果重载定义在类外,为了能够访问成员变量,就需要把成员变量修改为public类型;
//但是这种方法不够好。
//private:
	int _year;
	int _month;
	int _day;
};
//自定义类型,不能直接用各种运算符
//为了自定义类型可以使用各种运算符,制定了运算符重载的规operator 
//返回值 operator运算符 参数列表
bool operator==(Date d1,Date d2)
{
    return d1._year==d2._year
        &&d1._month==d2._month
        &&d1._day==d2._day;
}
//操作符重载的使用方式:
bool test(Date d1,Date d2)
{
    return d1==d2
}

而Python和Java习惯使用内置返回值函数。还有一种方法是使用有元,但是有元会破坏封装。

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //返回私有变量。
    int Getyear()
    {
        return _year;
    }
    //........
private:
	int _year;
	int _month;
	int _day;
};
bool operator==(Date d1,Date d2)
{
    return d1.Getyear()==d2.Getyear()
        &&d1.Getmonth()==d2.Getmonth()
        &&d1.Getday()==d2.Getday();
}
bool test(Date d1,Date d2)
{
    return d1==d2
}

2.1C++最常用的处理方法

C++最常用的是把运算符重载定义为成员函数

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    bool operator==(const Date&d)
    {
        return _year==d._year
        &&_month==d._month
        &&_day==d._day;
    }
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    Date d1(2022,5,17);
    Date d2(2022,5,17);
    if(d1==d2)
    {
        cout<<"=="<<endl;
    }
    return 0;
}
//输出:==

2.3日期类的赋值运算符重载

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //赋值运算符的返回值,返回值是赋值运算符的左值,因为赋值运算符要满足连续赋值 k=i=j j先赋值给i,i再赋值给k;
    //如果出了作用域对象还存在,可以使用引用返回;
    //返回值不可以使用const:比如(k=i)=j;如果返回值是const类型
    //那么(k=i)的返回值是const k,j就不能够对k进行下一步赋值。
    Date& operator=(const Date&d)
    {
        if(this!=&d)
       //如果使用if(*this!=d),就需要重载!=。得不偿失
        {
            _year=d._year;
            _month=d._month;
            _day=d._day;
            //对this指针进行解引用z
        }
        return *this;
    }
private:
	int _year;
	int _month;
	int _day;
};

2.4操作符重载的规则

对于该类有意义的运算符才进行运算符重载;比如日期类相加就无意义,所以就不需要操作符重载。

在这里插入图片描述

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-21 18:44:48  更:2022-05-21 18:45:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/14 1:09:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码