C++ Primer 学习笔记_26_操作符重载与转换(1)--可重载/不可重载的操作符、成员函数方式重载、友元函数方式重载

C++ Primer 学习笔记_26_操作符重载与转换(1)--可重载/不可重载的操作符、成员函数方式重载、友元函数方式重载

引言:

明智地使用操作符重载可以使类类型的使用像内置类型一样直观!

一、重载的操作符名

像任何其他函数一样,操作符重载函数有一个返回值和一个形参表。形参表必须具有操作符数目相同的形参。比如赋值时二元运算,所以该操作符函数有两个参数:第一个形参对应着左操作数,第二个形参对应右操作数。

大多数操作符可以定义为成员函数或非成员函数。当操作符为成员函数时,它的第一个操作数隐式绑定到this指针。有些操作符(比如赋值操作符)必须时类的成员函数。例如,赋值的this绑定到指向左操作数的指针。因此,赋值操作符接受单个形参,且该形参是同一类类型的对象。右操作数一般作为const引用传递。

并非所有的操作符都是可重载的,如下表:


可重载的操作符


+


-


*


/


%


^


&


|


~


!


,


=


<


>


<=


>=


++


--


<<


>>


==


!=


&&


||


+=


-=


/=


%=


^=


&=


|=


*=


<<=


>>=


[]


()


->


->*


new


new[]


delete


delete[]


不能重载的操作符


::


.*


.


?:

1、主要牢记不能重载的运算符:(可以这样加强记忆:带“点“的都不能重载)

作用域解析运算符::

条件运算符? :

直接成员访问运算符.

类成员指针引用的运算符 .*

sizeof运算符sizeof

注:.*是C++的类成员函数指针调用运算符,是用来调用一个类函数指针的。

举例:

假设有一个ClassA类拥有一个成员函数void ClassA::func(int i),则可以这么用:

ClassA obj;
fp = &ClassA::func; // 给这个类函数指针赋值
(obj.*fp)(5); // 这么用,相当于调用obj.func(5); 

2、运算符重载不允许发明新的运算符。

3、重载操作符必须具有一个类类型操作数

用于内置类型的操作符,其含义不能改变:

int operator+(int,int); //Error

重载操作符必须具有至少一个类类型或枚举类型的操作数。这条规则强制重载操作符不能重新定义用于内置类型对象的操作符的含义。

4、优先级和结合性是固定的

操作符的优先级、结合性或操作数数目不能改变。

【最佳实践】

重载逗号、取地址、逻辑与、逻辑或等操作符通常不是好做法!这些操作符都具有拥有的内置含义,如果我们定义了自己的版本,就不能再使用这些内置含义!

二、成员函数重载

成员函数原型的格式:

函数类型 operator 运算符(参数表); 

成员函数定义的格式:

函数类型 类名::operator 运算符(参数表)
{
     函数体;
}  

三、非成员函数重载

友元函数原型的格式:

friend 函数类型 operator 运算符(参数表);  

友元函数定义的格式:

函数类型 类名::operator 运算符(参数表)
{
    函数体;
}  

【一个实例:重载operator +】

#include<iostream>
using namespace std;  

class Complex
{
public:
    Complex(int real, int imag);
    Complex(void);
    ~Complex(void);  

    Complex &Add(const Complex &other);
    void Display() const;
    Complex operator+(const Complex &other);
    friend Complex operator+(const Complex &c1, const Complex &c2);  

private:
    int real_;
    int imag_;
};  

Complex::Complex(int real, int imag): imag_(imag), real_(real) { }
Complex::Complex(void) { }
Complex::~Complex(void) { }  

Complex &Complex::Add(const Complex &other)
{
    real_ += other.real_;
    imag_ += other.imag_;
    return *this;
}  

void Complex::Display() const
{
    cout << real_ << "+" << imag_ << "i" << endl;
}  

Complex Complex::operator+(const Complex &other)
{
    int r = real_ + other.real_;
    int i = imag_ + other.imag_;
    return Complex(r, i);
}  

Complex operator+(const Complex &c1, const Complex &c2) //和上一个函数等价,注释上一个函数,实现当前函数的效果也是一样的
{
//   int r = c1.real_ + c2.real_;
//   int i = c1.imag_ + c2.imag_;
//   return Complex(r, i);
}  

int main(void)
{
    Complex c1(3, 5);
    Complex c2(4, 6);
    c1.Add(c2);
    c1.Display();
    Complex c3 = c1 + c2; // 等价于c1.opertor+(c2); 或 operator+(c1, c2);
    c3.Display();
    return 0;
}  

运行结果:

7+11i

11+17i

解释:实现了Add成员函数,但c1.Add(c2); 改变的是c1 本身;如果我们想实现加号表达式,c3 = c1 + c2; 那么可以实现operator+ 运算符重载,可以是成员函数形式,也可以是友元形式,如果两者共存的话成员函数优先。

四、一些基本概念

(1)例1:下列运算符,在C++语言中不能重载的是()

A、*                B、?:             C、::             D、delete

解答:BC。

(2)例2:复制构造函数与赋值运算符的区别是什么?

解答:首先要说明的是,若用户没有定义,C++隐式声明一个复制构造函数和一个赋值运算符。二者很像,但是在下面这点有很大的不同:复制构造函数只在对象实例化时才会被调用,也就是说,在复制构造函数调用期间,这个对象处于一个未决状态(直到复制构造函数被成功调用),另外复制构造函数不返回任何值,void都没有。而赋值运算符则在一个现存的对象被赋予新的值时被调用,并且它有返回值。

并不是出现“=”就是调用赋值构造函数:

string book1("9-999-9"), book3;
string book2 = book1;    //调用复制构造函数,产生新对象book2
book3 = book1;    //调用赋值运算符,没有新的对象产生  

所以可以看是不是有新的对象产生,来决定到底是调用了复制构造函数,还是调用了赋值运算符。

关于复制构造函数,详情可参考:《blog:类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数 》

(3)例3:下列哪些函数可以被继承?

A、构造函数           B、析构函数            C、复制构造函数            D、赋值操作符重载函数

E、以上都可以           F、以上都不可以

解答:F。基类的构造函数(包括拷贝构造函数)、析构函数、赋值操作符重载函数,都不能被派生类继承。

(4)例4:C++中的空类默认产生了哪些成员函数?

解答:空类默认产生构造函数、复制构造函数、析构函数、赋值运算符重载函数、取址运算符重载函数、const取址运算符重载函数等。如下:

class Empty
{
public:
        Empty(); //默认构造函数
        Empty(const Empty&);  //复制构造函数
        ~Empty();  //析构函数
        Empty& operator=(const Empty&);  //赋值运算符
        Empty* operator&();  //取址运算符
        const Empty* operator&(() const;  //取址运算符const
};  

参考:

C++ primer 第四版

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-03 21:36:26

C++ Primer 学习笔记_26_操作符重载与转换(1)--可重载/不可重载的操作符、成员函数方式重载、友元函数方式重载的相关文章

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;

C++ Primer 学习笔记_26_类与数据抽象(12)--分别用C和C++来实现一个链栈

下面通过分别用C和C++来实现一个链栈(链表实现),从中体会数据封装抽象的思想: [C++实现] #include <iostream> using namespace std; class Stack { private: struct Link { int data_; Link *next_; Link(int data, Link *next) : data_(data), next_(next) { } }; public: Stack() : head_(0), size_(0)

C++ Primer 学习笔记_64_重载操作符与转换 --转换与类类型【下】

重载操作符与转换 --转换与类类型[下] 四.重载确定和类的实参 在需要转换函数的实参时,编译器自动应用类的转换操作符或构造函数.因此,应该在函数确定期间考虑类转换操作符.函数重载确定由三步组成: 1)确定候选函数集合:这些是与被调用函数同名的函数. 2)选择可行的函数:这些是形参数目和类型与函数调用中的实参相匹配的候选函数.选择可行函数时,如果有转换操作,编译器还要确定需要哪个转换操作来匹配每个形参. 3)选择最佳匹配的函数.为了确定最佳匹配,对将实参转换为对应形参所需的类型转换进行分类.对于

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

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

C++ Primer 学习笔记_28_操作符重载与转换(3)--成员函数的重载、覆盖与隐藏、类型转换运算符、*运算符重载、-&gt;运算符重载

C++ Primer 学习笔记_28_操作符重载与转换(3)--成员函数的重载.覆盖与隐藏.类型转换运算符.*运算符重载.->运算符重载 一.成员函数的重载.覆盖与隐藏 对于类层次的同名成员函数来说,有三种关系:重载.覆盖和隐藏,理清3种关系,有助于写出高质量的代码. 1.成员函数的重载 重载的概念相对简单,只有在同一类定义中的同名成员函数才存在重载关系,主要特点时函数的参数类型和数目有所不同:但不能出现函数参数的个数和类型均相同,仅仅依靠返回值类型不同来区分的函数,这和普通函数的重载是完全一致

C++ Primer 学习笔记_29_操作符重载与转换(4)--转换构造函数和类型转换运算符归纳、operator new 和 operator delete 实现一个简单内存泄漏跟踪器

C++ Primer 学习笔记_29_操作符重载与转换(4)--转换构造函数和类型转换运算符归纳.operator new 和 operator delete 实现一个简单内存泄漏跟踪器 一.转换构造函数 可以用单个实参来调用的构造函数定义从形参类型到该类型的一个隐式转换.如下: class Integral { public: Integral (int = 0); //转换构造函数 private: int real; }; Integral A = 1; //调用转换构造函数将1转换为In

C++ Primer 学习笔记_58_重载操作符与转换 --重载操作符的定义

重载操作符与转换 --重载操作符的定义 引言: 明智地使用操作符重载可以使类类型的使用像内置类型一样直观! 重载操作符的定义 重载操作符是具有特殊名称的函数:保留字operator后接定义的操作符符号.如: Sales_item operator+(const Sales_item &,const Sales_item &); 除了函数调用操作符之外,重载操作符的形参数目(包括成员函数的隐式this指针)与操作符的操作数数目相同.函数调用操作符可以接受任意数目的操作数. 1.重载的操作符名

C++ Primer 学习笔记_59_重载操作符与转换 --输入/输出、算术/关系操作符

重载操作符与转换 --输入/输出.算术/关系操作符 支持I/O操作的类所提供的I/O操作接口,一般应该与标准库iostream为内置类型定义的接口相同,因此,许多类都需要重载输入和输出操作符. 一.输出操作符<<的重载 为了与IO标准库一致,操作符应接受ostream&作为第一个形参,对类类型const对象的引用作为第二个形参,并返回ostream形参的引用! ostream &operator<<(ostream &os,const ClassType &

C++ Primer 学习笔记_63_重载操作符与转换 --转换与类类型【上】

重载操作符与转换 --转换与类类型[上] 引言: 在前面我们提到过:可以用一个实参调用的非explicit构造函数定义一个隐式转换.当提供了实参类型的对象需要一个类类型的对象时,编译器将使用该转换.于是:这种构造函数定义了到类类型的转换. 除了定义到类类型的转换之外,还可以定义从类类型到其他类型的转换.即:我们可以定义转换操作符,给定类类型的对象,该操作符将产生其他类型的对象.和其他转换一样,编译器将自动应用这个转换. 一.转换为什么有用? 定义一个SmallInt的类,该类实现安全小整数,这个