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

时间:2023-12-24 19:28:43

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,由于它要接受输入。另外,返回值最好是引用,避免了值拷贝过程。

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