详解C++赋值操作符重载

1.赋值操作符重载的原因

赋值操作符是一个使用频率最高的操作之一,通常情况下它的意义十分明确,就是将两个同类型的变量的值从一端(右端)传到另一端(左端)。但在以下两种情况下,需要对赋值操作符进行重载。
一是赋值号两边的表达式类型不一样,且无法进行类型转换。
二是需要进行深拷贝。

2. 赋值操作符重载的注意事项

赋值操作符只能通过类的成员函数的形式重载。这就说明了,如果要将用户自定义类型的值传递给基本数据类型的变量,只能通过类型转换机制,而不能利用重载来实现。

当赋值号两边的表达式不一致的时候,可能需要对赋值操作符进行重载,见下面的例子。

#include <iostream>
using namespace std;

class A
{
	int num;
public:
	A(){num=0;}
	A(int i){num=i;}
	void show(){
		cout<<num<<endl;
	}
};

int main(int argc, char* argv[])
{
	A a=5;		 //符值符号两边的数据类型不一样,这里表示创建新对象
	a.show();
	A a1;
	a1=1;    //赋值号两边的数据类型不一样,这是真正的赋值运算
	a1.show();
}

程序的输出结果是:

5
1

在语句A a=5中,虽然用到了“=”,但它的语义是构造一个类A的对象a,它等价于语句A a(5),所以该语句与赋值无关。而语句a1=1是一个真正的赋值语句,变量a1的类型是A,而常量1的类型是int,由于可以通过类A的构造函数A(int)将类型int转换成类型A(实际上是以int为参数构造了一个类A的临时对象),然后再完成赋值操作,所以不必再对赋值操作符进行重载。

3.深拷贝情况下对赋值操作符重载

深拷贝是对赋值操作符进行重载的一个因素。那么什么是深拷贝呢?简单的说,深拷贝是在把一个类对象a拷贝到另一个对象b中去时,如果对象a中包含非悬挂指针(野指针),那么要将a的指针所指区域的内容拷贝到b的相应指针所指的区域中去。进行深拷贝时,一般对象a和b有相同的数据类型。如果在进行赋值时发生深拷贝,就一定要对赋值操作符进行重载,否则赋值运算符就会按赋值的常规语义进行(成员变量之间传递数据),而不发生深拷贝。考察如下例子。

#include <iostream>
using namespace std;

class Student
{
	char* name;
	int age;
public:
	Student()
	{
		name=new char[20];
	}

	Student(char* n, int a)
	{
		name=new char[20];
		if(name) strcpy(name,n);
		age=a;
	}

	Student(const Student& s)
	{
		name=new char[20];
		*this=s;
	}

	void show()
	{
		cout<<"The student's name is "<<name;
		cout<<" and of age "<<age<<endl;
	}

	~Student()
	{
		delete[] name;
	}

	Student& operator=(const Student &s)
	{
		if(name) strcpy(name,s.name);
		age=s.age;
		return *this;
	}
};

int main()
{
	Student s1("张三",18),s4("李四",20);
	Student s2;
	s1.show();
	s2=s4;
	s2.show();
	Student s3=s1;
	s3.show();
	return 0;
}

程序的输出结果是:

The student's name is 张三 and of age 18
The student's name is 李四 and of age 20
The student's name is 张三 and of age 18

阅读以上程序,注意如下几点。

(1)由于在类Student中,存在指针成员name,所以,当两个Student类成员之间赋值时,必须使用深拷贝。执行s2=s4;语句,就是将s4对象赋值给s2,其中将s4.name字符串的内容拷入s2.name就是对深拷贝的具体体现。

(2)类的拷贝构造函数虽然与赋值操作符并不是一回事,但通常可以在拷贝构造函数中利用赋值操作符重载,以避免对两个对象之间传递数据的重复解释。

(3)上面的程序,直接使用strcpy(name,s.name);实现两个对象的字符串成员的数据传递。这是一种简化的做法,存在很多隐患。比如如果源字符串的长度超过20个字符,此程序会出现运行时错误。解决的办法是根据原字符串的长度,重新分配目的字符串的长度,再次之前还要释放目的字符串的空间。另外,一个对象赋值给自己,也会出现问题,需要进行源对象和目的对象地址的比较,再考虑赋不赋值。

(4)由于深拷贝会涉及到内存的动态分配和释放等一些较为复杂的操作,所以程序员在编写自定义类时要尽量避免深拷贝的出现。例如,在上例中,将成员变量name定义成string name,就可以避免自己编写实现深拷贝的代码。实际的深拷贝工作是由string类来完成,而string类是C++标准库提供的,我们可放心使用。

(5)最赋值操作符进行重载时,通常将操作符函数的返回值定义为赋值左操作数类型的引用。这是为了实现对赋值表达式的求值,还有一个目的就是为了实现链式操作。

以上就是详解C++赋值操作符重载的详细内容,更多关于C++赋值操作符重载的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++ 开发之实现操作符重载的实例

    C++操作符重载 实现效果图: 实例代码: Matrix.h #pragma once #include "vector" #include "iostream" #define rep(i,n) for(int i=1;i<=n;i++) //宏定义for循环,精简代码 using namespace std; class Matrix { public: //基本构造函数 Matrix(int Row=0, int Column=0); //拷贝构造函数或

  • C++中的操作符重载详细解析

    一.什么是操作符重载操作符重载可以分为两部分:"操作符"和"重载".说到重载想必都不陌生了吧,这是一种编译时多态,重载实际上可以分为函数重载和操作符重载.运算符重载和函数重载的不同之处在于操作符重载重载的一定是操作符.我们不妨先直观的看一下所谓的操作符重载: 复制代码 代码如下: #include <iostream> using namespace std; int main(){    int a = 2 , b = 3;    float c =

  • 有关C++中类类型转换操作符总结(必看篇)

    实例如下: class SmallInt { public: SmallInt(int i = 0): val(i) { if (i < 0 || i > 255) throw std::out_of_range("Bad SmallInt initializer"); } operator int() const { return val; } private: std::size_t val; }; 转换函数采用如下通用形式: operator type(); type

  • C++中复制构造函数和重载赋值操作符总结

    前言 这篇文章将对C++中复制构造函数和重载赋值操作符进行总结,包括以下内容: 1.复制构造函数和重载赋值操作符的定义: 2.复制构造函数和重载赋值操作符的调用时机: 3.复制构造函数和重载赋值操作符的实现要点: 4.复制构造函数的一些细节. 复制构造函数和重载赋值操作符的定义 我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数.析构函数.复制构造函数和重载赋值操作:即使在你没有明确定义的情况下,编译器也会给你生成这样的四个函数.例如以下类: 复制代码 代码如下: class CTes

  • C++ operator关键字(重载操作符)的用法详解

    operator是C++的关键字,它和运算符一起使用,表示一个运算符函数,理解时应将operator=整体上视为一个函数名. 这是C++扩展运算符功能的方法,虽然样子古怪,但也可以理解:一方面要使运算符的使用方法与其原来一致,另一方面扩展其功能只能通过函数的方式(c++中,"功能"都是由函数实现的). 一.为什么使用操作符重载? 对于系统的所有操作符,一般情况下,只支持基本数据类型和标准库中提供的class,对于用户自己定义的class,如果想支持基本操作,比如比较大小,判断是否相等,

  • c++中new和delete操作符用法

    "new"是C++的一个关键字,同时也是操作符.当我们使用关键字new在堆上动态创建一个对象时,它实际上做了三件事:获得一块内存空间.调用构造函数.返回正确的指针.当然,如果我们创建的是简单类型的变量,第二步就会被省略. new用法: 1. 开辟单变量地址空间 1)new int; 开辟一个存放数组的存储空间,返回一个指向该存储空间的地址.int *a = new int 即为将一个int类型的地址赋值给整型指针a. 2)int *a = new int(5) 作用同上,但是同时将整数

  • C++11新特性之自定义字面量

    1.示例 C++11新标准中引入了用户自定义字面量,也叫自定义后缀操作符,即通过实现一个后缀操作符,将申明了该后缀标识的字面量转化为需要的类型.考察如下代码: long double operator"" _mm(long double x) { return x / 1000; } long double operator"" _m(long double x) { return x; } long double operator"" _km(

  • C++ new、delete(new[]、delete[])操作符重载需要注意的问题

    new.delete(new[].delete[])操作符的重载需要注意: 1.重载的 new.delete(或者 new[].delete[])操作符必须是类的静态成员函数(为什么必须是静态成员函数,这很好理解,因为 new 操作符被调用的时候,对象还未构建)或者是全局函数,函数的原型如下: 复制代码 代码如下: void* operator new(size_t size) throw(std::bad_alloc); // 这里的 size 为分配的内存的总大小 void* operato

  • C++-操作符重载、并实现复数类详解

    首先回忆下以前学的函数重载 函数重载 函数重载的本质为相互独立的不同函数 通过函数名和函数参数来确定函数调用 无法直接通过函数名得到重载函数的入口地址 函数重载必然发生在同一个作用域中 类中的函数重载 静态成员函数能与普通成员函数建立重载关系 全局函数和成员函数不能构成重载关系 操作符重载(operator) 什么是操作符重载? 大家都知道,在C里,有'+,-,*,/'这些操作符,且它们的功能就是实现普通变量运算. 由于C++是面向对象的,遇到的变量大多都是对象,所以优化了C里的操作符,使它们拥

  • C++中的三大函数和操作符重载(Boolan)

    C++中三大函数: 析构函数 复制构造函数 =操作符(copy assignment operator) 这三个特殊的成员函数如果程序员没有实现,编译器将提供默认的实现方式. 析构函数: 形如-foo_t(),函数名和构造函数相同,前面加-,如果对象是自由变量创建,析构函数将在脱离作用域时调用.如果对象是通过new操作符创建的,则通过delete操作符调用析构函数. 复制构造函数: 形如foo_t(const foo_t& foo),以下情况复制构造函数均会被调用: 当对象按值返回时候(retu

  • 浅谈C++虚重载操作符 virtual operator= 的使用方法

    C++中虚操作符和其他虚函数的规则一样,操作符可以为虚函数,进行动态绑定, 虽然这种情况并不多见.本文以赋值操作符operator=举例. 派生类中要重定义基类虚函数,要注意参数必须为基类引用类型,否则与基类中虚函数是完全不同的,无法进行预期的动态绑定. 派生类除了重定义基类的虚操作符,还要定义自身的操作符重载.即派生层次每增加一层,理论上派生类就需要多定义一个操作符重载. 以下程序使用引用reference,通过指针调用赋值操作符(例:*p = value)情况是一样的. #include <

随机推荐