在C++中,将一个对象赋给另外一个对象,编译器将提供赋值运算符的定义。

有两种情况,下面假设catman是Monster的一个实例

第一种:初始化

Monster golblen= catman;

第二种:普通赋值

Monster golblen;

golblen= catman;

复制构造函数

其中,第一种情况,系统将调用复制构造函数,其原型为

Monster(const Monster& monster);

如果Monster里没有提供该函数,编译器将自动提供,编译器自动提供时

Monster monster3 = monster1;

相当于

Monster monster3;
monster3. name = monster1.name

由于monster3的name实际上共享monster1的name,所以这种复制叫做“浅复制”,两个对象共享同一份成员变量,在析构时会报错。

解决错误的方式是自己定义一个复制构造函数,并且自己为name开辟空间,将monster1的name使用strcpy_s的方式拷贝到自己开辟的空间里

顺带一提,monster2是用new创建的,new出来的对象存活于堆中,其他两个对象存活于栈中,系统只会自动释放栈内空间,而堆内空间需要用户自己维护。

Monster类声明

#pragma once
#include <iostream>; using std::ostream;
using std::cin;
using std::cout;
using std::endl; class Monster { private:
char* name; public:
Monster();
Monster(const char* name);
Monster(const Monster& monster);
~Monster();
friend ostream& operator<<(ostream& os, const Monster& monster);
};

Monster类定义

#include "Monster.h";

Monster::Monster()
{
cout << "默认构造函数执行完毕" << endl;
} Monster::Monster(const char* name)
{
this->name = new char[strlen(name) + ];
strcpy_s(this->name, strlen(name) + , name);
cout << "构造函数执行完毕" << endl;
} //复制构造函数,如果没有定义,编译器将自动提供浅复制方式的复制构造函数
Monster::Monster(const Monster& monster)
{
this->name = new char[strlen(monster.name) + ];
strcpy_s(this->name, strlen(monster.name) + , monster.name);
cout << "复制构造函数执行完毕" << endl;
} Monster::~Monster()
{
cout << name << "has been destroyed." << endl;
delete[] name;
}
ostream& operator<<(ostream& os, const Monster& monster)
{
return os << monster.name;
} int main(void)
{
{
Monster monster1("Golblen");
cout << "monster1: " << monster1 << " online" << endl; //下面这种初始化方式,将会调用复制构造函数
Monster monster2 = monster1;
cout << "monster2: " << monster2 << " online" << endl; Monster monster3;
monster3 = monster1;
cout << "monster3: " << monster2 << " online" << endl;
}
cin.get();
return ;
}

运行结果

报错了,看来赋值的时候,并没有调用自定义的复制构造函数,所以释放name时出错了

赋值运算符

解决上面报错的问题,自需要增加一个成员函数,

函数原型

Monster& operator=(const Monster& monstere);

函数定义

Monster& Monster::operator=(const Monster& monster)
{
if (this == &monster)
{
return *this;
}
delete[] this->name;
int length = strlen(monster.name);
name = new char[length+];
strcpy_s(name, length + , monster.name);
cout << "赋值运算符调用完毕" << endl;
return *this;
}

定义赋值运算符时,必须要做的三件事情:

1.由于目标对象已经引用了之前分配的数据,所以一定要使用delete[]来释放这些数据,否则将出现内存泄漏

2.函数应当避免将对象赋给自身,否则做第1步操作释放数据时,会将对象的数据也释放掉

3.函数应当返回一个指向当前对象的调用

另外,赋值运算符只能由类成员函数重载,为什么呢 (见https://bbs.csdn.net/topics/392049284?page=1 )

1:对于赋值操作符(=)--比较特别,因为任何类如果不提供显示的拷贝赋值(即重载=),则编译器会隐式地提供一个。这样的话,如果你再通过友元声明,进行全局的定义会造成调用二义性(即使允许,编译也会出错)

2:对于所有楼主提到的操作符(=,[],(),->),只能声明为成员函数是为了避免不合法的书写通过编译(这是推测出的原因,更深层的可能要研究 C++ 的设计了)

C++ 复制构造函数 与 赋值运算符的更多相关文章

  1. C++类的复制构造函数和赋值运算符

    前言: C++面向对象的编程过程中,凡是在类中运用到动态内存分配的时候总是会写一个显示的复制构造函数和赋值重载运算符,本文将结合C++ Primer Plus一书的内容分析下原因: 一.在C++编程中 ...

  2. C++学习之路(五):复制构造函数与赋值运算符重载

    之前没有细想过两者的区别,今天对此进行简要记录,后续完善补充. 复制构造函数是在类对象被创建时调用的,但是赋值运算符是被已经存在的对象调用完成赋值操作. 复制构造函数只在对象实例化时才被调用,即在复制 ...

  3. C++中复制构造函数与重载赋值操作符总结

    前言 这篇文章将对C++中复制构造函数和重载赋值操作符进行总结,包括以下内容: 1.复制构造函数和重载赋值操作符的定义: 2.复制构造函数和重载赋值操作符的调用时机: 3.复制构造函数和重载赋值操作符 ...

  4. C++:复制构造函数在什么时候被调用?

    这个问题不是疑问了,查了一下国外网站,总结一下.假设Person是一个类,复制构造函数的调用会在以下几种情况下发生: 1.对象在创建时使用其他的对象初始化 Person p(q); //此时复制构造函 ...

  5. C++中复制构造函数与重载赋值操作符

    我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数.析构函数.复制构造函数和重载赋值操作:即使在你没有明确定义的情况下,编译器也会给你生成这样的四个函数.例如以下类:   class CTe ...

  6. 剑指offer:赋值运算符函数和复制构造函数

    赋值运算符函数 对于定义一个赋值运算符函数时,需要注意一下几点: (1)函数的返回类型必须是一个引用,因为只有返回引用,才可以连续赋值 (2)传入的参数声明为常量引用,可以提高代码效率,同时赋值运算函 ...

  7. C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...

  8. c++的复制构造函数

    在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”): 1) 一个对象作为函数参数,以值传递的方式传入函数体: 2) 一个对象作为函数返回值,以值传递的方式从函数返回: 3) 一个 ...

  9. C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执行顺序和执行内容

    一.本文目的与说明 1. 本文目的:理清在各种继承时,构造函数.复制构造函数.赋值操作符.析构函数的执行顺序和执行内容. 2. 说明:虽然复制构造函数属于构造函数的一种,有共同的地方,但是也具有一定的 ...

随机推荐

  1. AD7729_双通道Sigma-Delta ADC

    sigma-delta adc的原理,就是通过一种结构把量化噪声调制到频谱的高端,也即对量化噪声而言,sdm是一个高通滤波器,而对基带信号则等价为一个全通滤波器,这样等价的基带信号的量化噪声就很小了, ...

  2. 当linux中的所有指令突然不能使用的时候

    接到同事电话,线上linux系统所有命令执行不了(由于其误操作执行一些命令) 此时可以按以下步骤解决问题: 1.首先导入临时变量(重启虚拟机之后失效),使得所有命令行暂时可以用 直接在命令行执行以下命 ...

  3. linux shell中 if else for循环以及大于、小于、等于逻辑表达式的历程

    作者:邓聪聪 比如比较字符串.判断文件是否存在及是否可读等,通常用"[]"来表示条件测试. 注意:这里的空格很重要.要确保方括号的空格.笔者就曾因为空格缺少或位置不对,而浪费好多宝 ...

  4. C#实现邮件发送的功能

    Ø  发送邮件所用的核心知识点 微软封装好的MailMessage类:主要处理发送邮件的内容(如:收发人地址.标题.主体.图片等等) 微软封装好的SmtpClient类:主要处理用smtp方式发送此邮 ...

  5. Hash之哈希表的详解

    Hash算法 Hash算法的原理; 决绝冲突的办法是: 线性探查法; 双散列函数法; 拉链法处理碰撞; 哈希原理及实现; 哈希表-Hash table, 也叫散列表;

  6. 使用percona-xtrabackup工具对mysql数据库的备份方案

    使用percona-xtrabackup工具对mysql数据库的备份方案 需要备份mysql的主机 172.16.155.23存放备份mysql的主机 172.16.155.22 目的:将155.23 ...

  7. 快速解决PHP调用Word组件DCOM权限的问题

    1. 首先必须要在电脑上安装 Office 2. windows+r : 输入 dcomcnfg.exe 打开组件服务,然后双击 组件服务 ==> 双击 计算机 ==> 双击 我的电脑 = ...

  8. MS SQL Server 增删改查

    数据插入 语法:INSERT INTO Table_name(field1,field2……fieldN) values(value1,vlaue2,…valueN) 单行插入用户类型 INSERT ...

  9. 【原创】Linux基础之gz文件相关操作

    gz文件不需要解压即可进行相关操作 $ zcat test.log.gz $ zmore test.log.gz $ zless test.log.gz $ zgrep '1.2.3.4' test. ...

  10. Gradle缓存目录文件命名规则

    在打开Android Studio项目的时候,会下载项目对应版本的gradle,该版本是在项目根目录下\gradle\wrapper\gradle-wrapper.properties文件中指定的: ...