拷贝构造函数和运算符重载(上)

简介: 拷贝构造函数和运算符重载(上)

拷贝构造函数

在创建对象的时候,是不是存在一种函数,使得能创建一个于已经存在的对象一模一样的新对象,那么接下来,让我们一起去了解一下,拷贝构造函数

特点

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

  • 拷贝构造函数是构造函数的重载
  • 拷贝构造函数的参数只有一个,且必须是类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用
  • 使用传值的方法是会报错,因为会引发无穷的递归调用
  • 若没有显示定义,编译器会生成默认的拷贝构造函数,默认的拷贝构造函数对象按照内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或值拷贝。

我们以日期类为例:

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

所以当拷贝构造函数的的调用要用传引用,因为传引用的话,就不需要拷贝,也就是说能调用并进入拷贝构造函数,当没有&的时候,形参进入就需要拷贝,就会调用拷贝构造函数(不进去,在形参处,就需要继续拷贝,从而无穷递归,使得编译器报错)

对于默认拷贝构造函数的使用:下面代码演示:

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;
  Date d2(d1);
  return 0;
}

  1. 内置类型成员完成浅拷贝or值拷贝
  2. 自定义类型成员会调用他的拷贝构造

举例说明

//1.全是内置成员变量
class Person{
    public:
      Person(int data,int age){
            _data=data;
            _age=age;
        }
    void Print()
    {
        cout<<_data<<" "<<_age<<endl;
    }
      int _data;
      int _age;
};
//这个会调用默认构造函数
int main(){
    Person p1(10,20);
    Person p2(p1);    
}
//2.都是自定义函数,或者说是包括自定义函数可以使用浅拷贝,因为我们需要的就是值的传递

但是对于这样一种需要自行开辟空间如malloc的自定义类型Stack,是需要进行深拷贝的,就是需要显示拷贝构造函数

原因如下:

以Stack为例,里面malloc一个空间作为动态内存的开辟,在实例化Stack对象之后,我们会自动构造这个Stack,最后用完这个对象之后会进行销毁,~Stack()析构函数里面是由free的,如果说,我们使用默认的拷贝构造函数之后,两个对象是指向同一空间的,但是,只能free一次,所以在第二次free的时候就会报错

所以对于这样的拥有动态内存的自定义类型,需要我们来进行深拷贝(自定义拷贝构造函数)

总结:

类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

分析拷贝构造函数情景

由下面的代码进行演示,在由拷贝构造函数的情况下的程序运行步骤

class Person{
  public:
    Person(int data){
        cout<<"Person构造函数"<<this<<endl;
    }
    Person(const Person& p){
        cout<<"拷贝构造函数"<<this<<endl;
    }
    ~person(){
        cout<<"Person析构函数"<<this<<endl;
    }
    int data;
};
Person Test(Person p){
    Person tmp(p);
    return tmp;
}
int main()
{
    Person p(10);
    Test(p);
    return 0;
}

为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用 尽量使用引用。

赋值运算符重载

赋值运算符重载可以使得自定义类型也可以像内置类型一样进行运算符运算,以下所有的运算符重载都是以Date类为例

运算符重载

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

格式:返回值类型 operator操作符(参数列表)

函数名为:operator+需要重载的运算符符号

注意事项:

1.不能通过连接其他符号来创建新的操作符:比如operator@需要是运算符,不是任意符号

2.重载操作符必须有一个类类型参数因为,我们就是对于自定义类型进行运算符运算(类似于内置类型)

3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

4.**.* :: sizeof ?: . ** 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

//具体的操作如下  以Date为例、
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;
};
// 这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
对于封装的问题,我们这里只能先使用public来解决,后面学到友元之后,就可以不用public
// 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。
bool operator==(const Date& d1, const Date& d2)
{
  return d1._year == d2._year
    && d1._month == d2._month
    && d1._day == d2._day;
}
void Test()
{
  Date d1(2018, 9, 26);
  Date d2(2018, 9, 27);
  cout << (d1 == d2) << endl;
}

我们当然可以例如上述代码一样,将operator==Date在类外面定义和声明,但是这样的话,我们如果不用友元的话,就只能将成员变量权限改为public,所以我们的方法为:将运算符重载放在Date里面

class Date
{
public:
  Date(int year = 1900, int month = 1, int day = 1)
  {
    _year = year;
    _month = month;
    _day = day;
  }
    bool operator==(const Date& d2)
  {
  return _year == d2._year
    && _month == d2._month
    && _day == d2._day;
  }
private:
  int _year;
  int _month;
  int _day;
};
//bool operator==(const Date& d1, const Date& d2)
//{
//  return d1._year == d2._year
//    && d1._month == d2._month
//    && d1._day == d2._day;
//}
void Test()
{
  Date d1(2018, 9, 26);
  Date d2(2018, 9, 27);
  cout << d1.operator==(d2) << endl;
  cout << (d1 == d2) << endl;
}
int main()
{
  Test();
  return 0;
}

bool operator==(const Date& d2)
  {
  return _year == d2._year
    && _month == d2._month
    && _day == d2._day;
  }
//所以左操作数就是this指针,右操作为形参


相关文章
|
3天前
|
数据采集 人工智能 安全
|
12天前
|
云安全 监控 安全
|
4天前
|
自然语言处理 API
万相 Wan2.6 全新升级发布!人人都能当导演的时代来了
通义万相2.6全新升级,支持文生图、图生视频、文生视频,打造电影级创作体验。智能分镜、角色扮演、音画同步,让创意一键成片,大众也能轻松制作高质量短视频。
1045 151
|
4天前
|
编解码 人工智能 机器人
通义万相2.6,模型使用指南
智能分镜 | 多镜头叙事 | 支持15秒视频生成 | 高品质声音生成 | 多人稳定对话
|
17天前
|
机器学习/深度学习 人工智能 自然语言处理
Z-Image:冲击体验上限的下一代图像生成模型
通义实验室推出全新文生图模型Z-Image,以6B参数实现“快、稳、轻、准”突破。Turbo版本仅需8步亚秒级生成,支持16GB显存设备,中英双语理解与文字渲染尤为出色,真实感和美学表现媲美国际顶尖模型,被誉为“最值得关注的开源生图模型之一”。
1732 9
|
9天前
|
人工智能 自然语言处理 API
一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸
一句话生成拓扑图!next-ai-draw-io 结合 AI 与 Draw.io,通过自然语言秒出架构图,支持私有部署、免费大模型接口,彻底解放生产力,绘图效率直接爆炸。
682 152
|
11天前
|
人工智能 安全 前端开发
AgentScope Java v1.0 发布,让 Java 开发者轻松构建企业级 Agentic 应用
AgentScope 重磅发布 Java 版本,拥抱企业开发主流技术栈。
646 12
|
6天前
|
SQL 自然语言处理 调度
Agent Skills 的一次工程实践
**本文采用 Agent Skills 实现整体智能体**,开发框架采用 AgentScope,模型使用 **qwen3-max**。Agent Skills 是 Anthropic 新推出的一种有别于mcp server的一种开发方式,用于为 AI **引入可共享的专业技能**。经验封装到**可发现、可复用的能力单元**中,每个技能以文件夹形式存在,包含特定任务的指导性说明(SKILL.md 文件)、脚本代码和资源等 。大模型可以根据需要动态加载这些技能,从而扩展自身的功能。目前不少国内外的一些框架也开始支持此种的开发方式,详细介绍如下。
408 4