C++学习笔记_8_ 类的成员函数-拷贝复制

本文详细介绍了C++中的拷贝构造函数和赋值重载的概念及特性。拷贝构造是创建与已有对象相同的新对象的过程,而赋值重载则是为自定义类型重载赋值操作符的行为,允许自定义赋值行为。文章通过代码示例解释了这两个概念,并强调了在资源管理中正确实现拷贝构造和赋值重载的重要性。
摘要由CSDN通过智能技术生成

一、拷贝构造

在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。
在创建对象时,也可以创建一个与一个对象一模一样的新对象。

1.概念

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

2.特性

拷贝构造函数也是特殊的成员函数,其特性如下:

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须使用引用传参。(使用传值方式会引发无穷递归调用)

代码示例:

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;
 _month = d._month;
 _day = d._day;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d1;
 Date d2(d1);
 return 0; }

此时,d2对d1完成拷贝构造

  1. 显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做拷贝,或者拷贝。
    代码示例:
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;
};
int main()
{
 Date d1;
 // 这里d2调用的默认拷贝构造完成拷贝,d2和d1的值也是一样的
 Date d2(d1);
 return 0; }

二、赋值重载

具体用法通过日期类进行练习

1.运算符重载

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

函数名:关键字operator + 需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
———————————————————————
注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型或者枚举类型的操作数
  • 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
  • . 、* 、::sizeof?: 以上5个运算符不能重载。

2.赋值运算符

  1. 参数类型
  2. 返回值
  3. 检测是否自己给自己赋值
  4. 返回*this
  5. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。

补充

在C++中,赋值重载(或称为赋值操作符重载)是指为自定义类型(如类)重载赋值操作符(=)的行为。默认情况下,C++中的赋值操作符对于内置类型(如int、float、char等)是有效的,但当创建自定义类型时,可能需要定义自己的赋值行为。

赋值操作符重载函数通常具有以下形式:

ClassName& operator=(const ClassName& other);

这里有几个关键点:

  • 返回类型:通常返回对调用对象的引用(ClassName&),这允许连续赋值(链式赋值)。
  • 参数:通常接受一个常量引用参数,该参数是待赋值对象的副本。
  • 自赋值检查:在赋值操作符的实现中,通常需要检查调用对象(*this)和参数对象(other)是否是同一个对象,以避免自赋值导致的潜在问题。
  • 资源释放:如果类管理资源(如动态分配的内存),则在赋值之前可能需要释放这些资源。
  • 资源分配:将参数对象的值复制到调用对象中,可能需要分配新的资源。
  • 返回*this:最后,返回调用对象的引用,以支持链式赋值。
    以下是一个简单的示例,展示了如何为一个管理动态数组的类重载赋值操作符:
#include <iostream>  
  
class MyArray {  
private:  
    int* data;  
    size_t size;  
  
public:  
    // 构造函数、析构函数、拷贝构造函数等...  
  
    // 赋值操作符重载  
    MyArray& operator=(const MyArray& other) {  
        // 自赋值检查  
        if (this == &other) {  
            return *this;  
        }  
  
        // 释放当前对象的资源  
        delete[] data;  
  
        // 分配新资源并复制数据  
        size = other.size;  
        data = new int[size];  
        for (size_t i = 0; i < size; ++i) {  
            data[i] = other.data[i];  
        }  
  
        // 返回调用对象的引用  
        return *this;  
    }  
  
    // 其他成员函数...  
};  
  
int main() {  
    MyArray arr1{/* 初始化arr1 */};  
    MyArray arr2{/* 初始化arr2 */};  
  
    // 使用重载的赋值操作符  
    arr1 = arr2;  
  
    // 链式赋值(如果其他类也重载了赋值操作符)  
    MyArray arr3;  
    arr3 = arr1 = arr2; // 先执行arr1 = arr2,然后执行arr3 = arr1的结果  
  
    return 0;  
}

请注意,在实际应用中,你可能还需要考虑异常安全性、性能优化和深拷贝与浅拷贝的问题。此外,如果类管理资源,通常还需要实现拷贝构造函数、移动构造函数、拷贝赋值操作符和移动赋值操作符,以提供完整的资源管理语义。这通常被称为“规则五”(Rule of Five)或“规则零”(Rule of Zero),后者推荐使用智能指针(如std::unique_ptr或std::shared_ptr)来自动管理资源。**

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

明教张公子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值