C++ Primer笔记10_运算符重载_赋值运算符_进入/输出操作符

1.颂值运营商

首先来福值运算符引入后面要说的运算符重载。上一节说了构造函数、拷贝构造函数;一个类要想进行更好的控制。须要定义自己的构造函数、拷贝构造函数、析构函数、当然,还有赋值运算符。常说的三大函数就是指拷贝、赋值、析构。

假设一个类不定义自己的赋值运算符。会自己生成一个默认的赋值运算操作。这个默认的赋值运算满足一般类的需求。它实现的是一个浅拷贝。可是当类的功能、作用逐渐完好时,就会出现非常多问题。所以,通过自己定义赋值运算符来控制赋值操作时类的行为是非常有必要的。当一个类的对象与对象之间发生赋值(=)运算时,就会调用重载的赋值运算符函数。

还是以上节的样例来说。看代码:

#include <iostream>
#include <new>
#include <string>

using namespace std;

class Person
{
public:
	Person();
	Person(int n, const string &str);
	Person(const Person &n);
	Person &operator=(const Person &p);//赋值运算符函数
	~Person();
private:
	int age;
	string *name;
};

Person::Person():age(0), name(NULL)
{
	cout << "Default Person" << endl;
}

Person::Person(int n, const string &str):age(n), name(new string(str))
{
	cout << "Init Person" << endl;
}

Person::Person(const Person &n)
{
	if(n.name)
	{
		name = new string(*(n.name));
		age = n.age;
	}
	else
	{
		name = NULL;
		age = 0;
	}
}

Person & Person::operator=(const Person &p)
{
	if(this == &p)
	{
		return *this;//推断传入的对象是否是当前对象本身
	}
	string *tmp = new string(*p.name);//又一次分配一段空间
	delete this->name;//释放原空间
	this->name = tmp;
	this->age = p.age;

	return *this;
}

Person::~Person()
{
	cout << "~Person " << "name: " << name << " age: " << age << endl;
	delete name;
}

int main()
{
	Person p1(20, "SCOTT");
	Person p2;
	p2 = p1;

	return 0;
}

在这里,假设没有自己定义赋值运算符,当执行P2 = P1时,是没有错的。而错误会处在析构部分,上一篇文章中已经说明原因。

Init Person

Default Person

~Person name: 0x8264020 age: 20

~Person name: 0x8264020 age: 20

Segmentation fault (core dumped)——段错误!

自己定义赋值运算符之后。执行结果例如以下:

Init Person

Default Person

~Person name: 0x8121030 age: 20

~Person name: 0x8121020 age: 20

注意:

1)一般来说,大多数赋值运算符函数组合了析构函数和拷贝构造函数的工作。

2)假设讲一个对象赋予它自身。赋值运算符必须能正确的工作。

3)当编写赋值运算符函数时。一个好的模式是将右側对象复制到一个局部暂时对象中。拷贝完毕后,销毁左側对象的现有成员就是安全的了。

然后将数据从暂时对象复制到左側对象。

2.运算符重载

上面是众多运算符重载的一个实例。重载的运算符是具有特殊名字的函数:它们的名字是由keywordoperator和其后要定义的运算符号共同组成。

它与普通函数一样也有返回值、參数列表、以及函数体。

重载运算符函数的參数数量与该运算符的作用的运算对象数量一样多。一元运算符有一个參数。二元运算符有两个。

对于二元运算符来说,左側运算对象传递给第一个參数,右側运算对象传递给第二个參数。

假设一个运算符函数是成员函数,则它的第一个(左側)运算对象绑定到隐式的this指针上。因此,成员运算符函数的參数比运算符的运算对象少一个。(如上例中的=运算符)

2.1 输入、输出运算符

IO标准库分别使用>>和<<运行输入和输出操作。

类能够按须要来自己定义输入、输出运算符。

样例:

#include <iostream>
#include <new>
#include <string>

using namespace std;

class Person
{
public:
	Person();
	Person(int n, const string &str);
	Person(const Person &n);
	Person &operator=(const Person &p);
	~Person();

	string getName()const;
	friend ostream &operator<<(ostream &out, const Person &p);//输出运算符
	friend istream &operator>>(istream &in, Person &p);//输入运算符
private:
	int age;
	string *name;
};

Person::Person():age(0), name(NULL)
{
	cout << "Default Person" << endl;
}

Person::Person(int n, const string &str):age(n), name(new string(str))
{
	cout << "Init Person" << endl;
}

Person::Person(const Person &n)
{
	if(n.name)
	{
		name = new string(*(n.name));
		age = n.age;
	}
	else
	{
		name = NULL;
		age = 0;
	}
}

Person & Person::operator=(const Person &p)
{
	if(this == &p)
	{
		return *this;
	}
	string *tmp = new string(*p.name);
	delete this->name;
	this->name = tmp;
	this->age = p.age;

	cout << "operator =" << endl;

	return *this;
}

Person::~Person()
{
	cout << "~Person " << "name: " << name << " age: " << age << endl;
	delete name;
}

string Person::getName()const
{
	if(name)
	{
		return *name;
	}
	return string();
}
//重载输出运算符
ostream &operator<<(ostream &out, const Person &p)
{
	out << "p.age: " << p.age << ", p.name: " << p.getName();
	return out;
}
//重载输入运算符
istream &operator>>(istream &in, Person &p)
{
	string s;
	cout << "please input age and name:";
	in >> p.age >> s;
	if(in)//推断是否读取正确
	{
		p.name = new string(s);
	}
	else
	{
		p = Person();
	}

	return in;
}

int main()
{
	Person p1(20, "SCOTT");
	Person p2(10, "Kate");
	Person p3;
/*
	cout << p1 << endl;
	cout << p2 << endl;
	cout << p3 << endl;
	*/
	cin >> p3;
	cout << p3 << endl;

	return 0;
}

执行程序,输入12 Mike, 结果例如以下:

Init Person

Init Person

Default Person

please input age and name:12 Mike

p.age: 12, p.name: Mike

~Person name: 0x939a088 age: 12

~Person name: 0x939a048 age: 10

~Person name: 0x939a020 age: 20

总结:

1.输入、输出运算符必须是非成员函数。

2.输入、输出运算符一般声明为友元类型。

3.输出运算符函数中第二个參数能够声明为const型,由于不须要改变其值。而输入运算符的第二个參数不能为const,由于它要接受输入。另外,返回值最好是引用,避免了值拷贝过程。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-08-26 06:20:25

C++ Primer笔记10_运算符重载_赋值运算符_进入/输出操作符的相关文章

C++ Primer笔记10_运算符重载_赋值运算符_输入/输出运算符

我们致力于推广Scriptcase,以产品销售.技术支持.培训服务.外包开发为核心,将Scriptcase这一简单易用的工具真正在国内予以推广.我们搭建了Scriptcase的示例网站,通过该网站可以看到在几乎不需要任何编程的情况下,Scriptcase可以做到如何快速和先进的开发. 敬请访问 www.phpscriptcase.com C++ Primer笔记10_运算符重载_赋值运算符_输入/输出运算符

C++ Primer笔记12_运算符重载_递增递减运算符_成员访问运算符

1.递增递减运算符 C++语言并不要求递增递减运算符必须是类的成员.但是因为他们改变的正好是所操作对象的状态,所以建议设定为成员函数. 对于递增与递减运算符来说,有前置与后置两个版本,因此,我们应该为类定义两个版本的递增与递减运算符. 问题来了,程序是如何区分前置和后置呢?因为都是++和-- 为了解决这个问题,后置版本的递增递减运算符接受一个额外的(不被使用)int类型的形参.当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参.这个形参唯一的作用就是区分前置和后置运算符函数. 因为不会

C++ Primer笔记13_运算符重载_总结

总结: 1.不能重载的运算符: . 和 .* 和 ?: 和 ::  和 sizeof 和 typeid 2.重载运算符有两种基本选择: 类的成员函数或者友元函数, 建议规则如下: 运算符 建议使用 所有一元运算符 成员函数 = () [] -> 必须是成员函数 += -= /= *= ^= &= != %= >>= <<= , 似乎带等号的都在这里了. 成员函数 所有其它二元运算符, 例如: –,+,*,/ 友元函数 3.前几篇中的实例,现在汇总Person类的程序:

C++ Primer笔记12_运算符重载_递增递减运算符_成员訪问运算符

1.递增递减运算符 C++语言并不要求递增递减运算符必须是类的成员.可是由于他们改变的正好是所操作对象的状态.所以建议设定为成员函数. 对于递增与递减运算符来说,有前置与后置两个版本号,因此.我们应该为类定义两个版本号的递增与递减运算符. 问题来了.程序是怎样区分前置和后置呢?由于都是++和-- 为了解决问题,后置版本号的递增递减运算符接受一个额外的(不被使用)int类型的形參.当我们使用后置运算符时,编译器为这个形參提供一个值为0的实參. 这个形參唯一的作用就是区分前置和后置运算符函数. 由于

C++ Primer笔记11_运算符重载_算术/关系运算符_下标运算符

3.算法综合实践——搜索引擎 上网搜索有关“搜索引擎”的相关资料,包括但不限于以下方面(至少要有2个方面):搜索引擎岗位要求.搜索引擎工作原理.搜索引 擎涉及到教材中哪些算法.搜索引擎的盈利模式.搜索引擎源码链接.国内外搜索引擎公司现状等. <1>搜索引擎指自动从因特网搜集信息,经过一定整理以后,提供给用户进行查询的系统.因特网上的信息浩瀚万千,而且毫无秩序,所有的信息像汪洋上的一个个小岛,网页链接是这些小岛之间纵横交错的桥梁,而搜索引擎,则为用户绘制一幅一目了然的信息地图,供用户随时查阅.

C++ Primer 学习笔记_27_操作符重载与转换(2)--++/--运算符重载、!运算符重载、赋值运算符重载 、String类([]、 +、 += 运算符重载)、&gt;&gt;和&lt;&lt;运算符重载

C++ Primer 学习笔记_27_操作符重载与转换(2)--++/--运算符重载.!运算符重载.赋值运算符重载 .String类([]. +. += 运算符重载).>>和<<运算符重载 一.++/--运算符重载 1.前置++运算符重载 成员函数的方式重载,原型为: 函数类型 & operator++(); 友元函数的方式重载,原型为: friend 函数类型 & operator++(类类型 &); 2.后置++运算符重载 成员函数的方式重载,原型为:

C++ Primer Plus学习笔记之运算符重载

C++ Primer Plus学习笔记之运算符重载 1,成员函数和友元函数选择的建议 下面我们先看两个例子: 成员函数重载 #include<iostream> using namespace std; class Complex { public: Complex(double r=0,double i=0) { re=r; im=i; } Complex operator+(const Complex& obj); Complex operator!(); void Display

C++学习笔记之运算符重载

一.运算符重载基本知识 在前面的一篇博文 C++学习笔记之模板(1)——从函数重载到函数模板 中,介绍了函数重载的概念,定义及用法,函数重载(也被称之为函数多态)就是使用户能够定义多个名称相同但特征标(参数列表)不同的函数,目的是在对不同类型的参数执行相同的操作时只用一个同名的函数. 运算符重载,就是使同一个运算符在面临不同类型的数据时作出不同的操作(函数重载是操作相同),就是让同一个运算符有多重功能.实际上我们经常用的许多运算符已被重载,例如,将*用于地址,将得到存储在这个地址中的值:但将它用

PKU C++程序设计实习 学习笔记4 运算符重载

第四章 运算符重载 4.1 运算符重载的基本概念 1. 运算符 2. 自定义数据类型与运算符重载 C++提供了数据抽象的手段:用户自己定义数据类型 -- 类 ? 调用类的成员函数->操作它的对象 类的成员函数->操作对象时,很不方便 ? 在数学上,两个复数可以直接进行+/-等运算 Vs. 在C++中,直接将+或-用于复数是不允许的 3. 运算符重载 对抽象数据类型也能够直接使用C++提供的运算符 ? 程序更简洁 ? 代码更容易理解 运算符重载 ? 对已有的运算符赋予多重的含义 ? 使同一运算符