在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. AUTOML --- Machine Learning for Automated Algorithm Design.

    自动算法的机器学习: Machine Learning for Automated Algorithm Design. http://www.ml4aad.org/ AutoML——降低机器学习门槛的 ...

  2. WARN: Establishing SSL connection

    在我们使用连接MySQL数据库时会出现这样的提示: Tue Aug 29 13:24:29 CST 2017 WARN: Establishing SSL connection without ser ...

  3. HTML5 播放器

    之前一个前端群里 大牛 做了一个自适应的HMLT5播放器 最近根据其思路做了一个相对单一移动端的demo,demo用的图片和歌曲json的数据设计 都是群里大牛做的,在这谢谢~: 同时借鉴的几篇文章: ...

  4. 通过python操作smtplib模块发送邮件

    # gconf.py SMTP_SERVER_HOST='smtp.exmail.qq.com' SMTP_SERVER_PORT=25 SMTP_USER='jack@qq.com' # 邮箱客户端 ...

  5. UDP/TCP拾遗

    1.UDP的特点 (1)UDP 是无连接的,即发送数据之前不需要建立连接. (2)UDP 使用尽最大努力交付,即不保证可靠交付,同时也不使用拥塞控制. (3)UDP 是面向报文的.UDP 没有拥塞控制 ...

  6. ASP.NET如何下载大文件

    关于此代码的几点说明: 1. 将数据分成较小的部分,然后将其移动到输出流以供下载,从而获取这些数据. 2. 根据下载的文件类型来指定 Response.ContentType .(参考OSChina的 ...

  7. Go语言从入门到放弃(一) 变量/常量/函数

    HelloWorld 我们先看看一个最简单的HelloWorld代码 package main import "fmt" func main() { fmt.Println(&qu ...

  8. Affiliate实战记录之一:CPI、CPA、CPM...名词解释

    1.CPM (Cost Per Mille,或者Cost Per Thousand;Cost Per Impressions) 每千人成本,按展示次数收费 网上广告收费最科学的办法是按照有多少人看到你 ...

  9. C# Excel行高、列宽、合并单元格、单元格边框线、冻结

    private _Workbook _workBook = null;private Worksheet _workSheet = null;private Excel.Application _ex ...

  10. 【MySql】删除操作

    删除表内数据,用 delete.格式为: delete from 表名 where 删除条件; 实例:删除学生表内姓名为张三的记录. delete from student where T_name ...