资料来源:南科大 于仕琪 C/C++ Program Design
LINK:CPP/week10 at main · ShiqiYu/CPP · GitHub
一、本节内容
本节首先以一个例子具体演示和回顾操作符重载、友元函数以及重载<<操作符。习题部分则为各种运算符重载以及输入输出重载
1.1 Operator overloading 运算符重载
1.2 member function, non-member function, friend function 成员函数 非成员函数 友元函数
- 成员函数是定义在类内部的函数。它们可以访问类的所有成员变量和其他成员函数。成员函数可以是普通函数、静态函数或虚函数
- 非成员函数是在类外部定义的函数。它们不能直接访问类的私有或保护成员,除非通过公有成员函数或友元机制。非成员函数通常用于操作多个类对象或实现某些独立于类的功能
- 友元函数是被类声明为友元的非成员函数。友元函数可以访问类的私有和保护成员。友元函数通常用于需要直接访问类的内部数据但又不适合作为成员函数的情况
- 对=进行重构必须是成员函数
友元函数的优势:
相对于成员函数:
- 灵活性:友元函数可以定义在类的外部,这使得它们在处理多个类对象时更加灵活。例如,运算符重载函数可以作为友元函数定义,以便处理不同类型的操作数。
- 类型转换:友元函数可以对左操作数进行类型转换,这在成员函数中是不可能的。对于需要对左操作数进行类型转换的运算符重载,友元函数是一个理想的选择。
- 减少类的耦合:友元函数可以减少类之间的耦合度。通过友元函数,一个类可以允许另一个类或函数访问其私有成员,而不需要将这些成员公开。这有助于保持类的封装性,同时提供必要的访问权限。
相较于其他非成员函数:
- 访问私有和保护成员:友元函数可以访问类的私有和保护成员,而不需要通过公共接口。这使得友元函数在需要直接操作类的内部数据时非常有用。
总结:可以理解为友元函数占有了二者的优势。同时,友元函数是非成员函数。
- 补充:友元类
除了友元函数,C++还支持友元类。友元类的所有成员函数都可以访问另一个类的私有和保护成员。
- 定义:友元类是被另一个类声明为友元的类。友元类的所有成员函数都可以访问该类的私有和保护成员。
- 作用范围:友元类的所有成员函数都可以访问它被声明为友元的那个类的私有和保护成员。
- 使用场景:友元类通常用于两个类需要紧密协作的情况。例如,一个类需要频繁访问另一个类的内部数据。
对比友元函数和友元类:
- 友元函数:适用于单个函数需要访问类的私有成员的情况。
- 友元类:适用于一个类的多个成员函数需要访问另一个类的私有成员的情况。
一个例子:
1.3 Member-wise initialization 初始化
在使用现有对象对新的对象进行初始化过程中,如果没有写相关的构造函数以提供复制功能,编译器将会自动提供一个默认的以实现复制初始化。
1.4 Objects assignment 赋值
在给对象赋值过程中不会调用复制的构造函数。必须对“=”运算符进行重构以实现赋值功能。
二、习题笔记
- 成员函数:赋值运算符 (=) 必须定义为成员函数。
- 友元函数:输入 (>>) 和输出 (<<) 运算符必须定义为友元函数。
- 其他运算符(如加法、减法、乘法、等于和不等于)可以根据具体需求选择定义为友元函数或成员函数。如果都是用类的对象进行运算,则可以全部定义为成员函数,如果存在int/char/double和类混合运算情况,则定义为友元函数较好。
main.cpp
#include "complex.hpp"int main() {Complex c1(3.0, 4.0), c2(1.0, 2.0), c3;bool c;cout << "c1: " << c1 << endl;cout << "c2: " << c2 << endl;c = c1 == c2;cout << "c1 和 c2 相等? " << c << endl;c = c1 != c2;cout << "c1 和 c2 不相等? " << c << endl;c3 = c1 + c2;cout << "c1 + c2: " << c3 << endl;c3 = c1 - c2;cout << "c1 - c2: " << c3 << endl;c3 = c1 * c2;cout << "c1 * c2: " << c3 << endl;cout << "输入一个复数 (c3):" << endl;cin >> c3;cout << "你输入的是: " << c3 << endl;return 0;
}
complex.hpp
#pragma once#include <iostream>
using namespace std;class Complex
{private:double realPart;double imaginaryPart;public:Complex() : realPart(0.0), imaginaryPart(0.0) {}Complex(double real, double imaginary) : realPart(real), imaginaryPart(imaginary) {}Complex& operator=(const Complex &other) {if (this != &other) {realPart = other.realPart;imaginaryPart = other.imaginaryPart;}return *this;}// the declarations, the definitions are out of the class// 显示复数friend Complex operator+(const Complex &lhs, const Complex &rhs);friend Complex operator-(const Complex &lhs, const Complex &rhs);friend Complex operator*(const Complex &lhs, const Complex &rhs);friend bool operator==(const Complex &lhs, const Complex &rhs);friend bool operator!=(const Complex &lhs, const Complex &rhs);friend std::ostream& operator<<(std::ostream &out, const Complex &c);friend std::istream& operator>>(std::istream &in, Complex &c);};
complex.cpp
#include "complex.hpp"Complex operator+(const Complex &lhs, const Complex &rhs)
{return Complex(lhs.realPart + rhs.realPart, lhs.imaginaryPart + rhs.imaginaryPart);
}Complex operator-(const Complex &lhs, const Complex &rhs)
{return Complex(lhs.realPart - rhs.realPart, lhs.imaginaryPart - rhs.imaginaryPart);
}Complex operator*(const Complex &lhs, const Complex &rhs)
{return Complex(lhs.realPart * rhs.realPart - lhs.imaginaryPart * rhs.imaginaryPart, lhs.realPart * rhs.imaginaryPart + lhs.imaginaryPart * rhs.realPart);
}bool operator==(const Complex &lhs, const Complex &rhs)
{return (lhs.realPart == rhs.realPart && lhs.imaginaryPart == rhs.imaginaryPart);
}bool operator!=(const Complex &lhs, const Complex &rhs)
{return !(lhs == rhs);
}std::ostream& operator<<(std::ostream &out, const Complex &c)
{if(c.imaginaryPart >= 0) out << c.realPart << " + " << c.imaginaryPart << "i";elseout << c.realPart << " - " << -c.imaginaryPart << "i";return out;
}std::istream& operator>>(std::istream &in, Complex &c)
{std::cout << "输入实部: ";in >> c.realPart;std::cout << "输入虚部: ";in >> c.imaginaryPart;return in;
}
运行结果:
编程细节tips:
- 友元函数的声明通常放在类的任何访问控制区域(public、protected 或 private)都可以。这是因为友元函数的访问权限与它们在类中的声明位置无关。
- 赋值运算符 operator= 通常返回一个对当前对象的引用(Complex&),这是为了支持链式赋值操作,并确保赋值操作的效率和正确性。
1. 支持链式赋值:返回当前对象的引用允许链式赋值操作。例如:
Complex c1, c2, c3;
c1 = c2 = c3;
在这个例子中,c2 = c3 返回 c2 的引用,然后 c1 = c2 使用这个引用进行赋值。如果赋值运算符不返回引用,链式赋值将无法正常工作。
2. 提高效率:返回引用避免了不必要的对象拷贝。赋值运算符直接返回当前对象的引用,而不是返回一个新的对象,从而提高了效率。
3. 符合C++标准惯例:返回当前对象的引用是C++标准库中赋值运算符的惯例。这种做法确保了代码的一致性和可读性。