在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. 题解-hdu2866 Special Prime

    Problem hdu-2866 题意:求区间\([2,L]\)有多少素数\(p\)满足\(n^3+pn^2=m^3\),其中\(n,m\)属于任意整数 Solution 原式等价于\(n^2(p+n ...

  2. 【转】线程join()方法的含义

    在很多情况下,主线程生成并启动了子线程,如果子线程里要进行大量的耗时运算,主线程往往将于子线程之前结束,但是如果主线程处理完其它事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后 ...

  3. 2.4G和5G的Wi-Fi各自优缺点对比

    原文地址:http://service.tp-link.com.cn/detail_article_3366.html 1.为什么5G信号的穿墙效果比2.4G信号差? 与路由器的距离相同时,5G信号相 ...

  4. $Django importlib与dir知识,手写配置文件, 配置查找顺序 drf分页器&drf版本控制

    1  importlib与dir知识 # importlib简介动态导入字符串模块 # 常规导入 from ss.aa import b from ss import a print(b,type(b ...

  5. POJ 3368

    题意: 给你一组数列, 查询区间内有出现次数最多的数的频数 RMQ , 对于一个区间, 分为两部分, 从  L 开始连续到  T  , T + 1 到  R 显然 答案为  MAX (T – L + ...

  6. word发布博客

    无向图双连通部件(双连通分量) 关节点和桥边的定义: 双连通部件的性质   每一个双连通部件应该包含至少两个顶点,除非整个无向图只包含一个顶点   如果两个双连通部件包含同一个顶点,那么这个共有的顶点 ...

  7. Java二维码生成与解码

      基于google zxing 的Java二维码生成与解码   一.添加Maven依赖(解码时需要上传二维码图片,所以需要依赖文件上传包) <!-- google二维码工具 --> &l ...

  8. ls 命令

    命令: ls 对应英文: list 作用: 查看当前文件夹下的内容 选项: -a 查看隐藏文件, 文件名前带 . 号的文件 -l 以详细列表形式显示 -h 人性化方式显示文件大小(注:-h必须和-l同 ...

  9. JGroups

    JGroups Developer(s) Bela Ban Stable release 4.0.10.Final / February 1, 2018 Written in Java Operati ...

  10. mybatis xml中不能直接用大于号、小于号要用转义字符

    2.使用 <![CDATA[ ]]>标记