首页 > 编程语言 >从0开始C++(五):友元函数&运算符重载

从0开始C++(五):友元函数&运算符重载

时间:2024-06-22 09:02:37浏览次数:26  
标签:友元 函数 成员 C++ 运算符 重载 MyClass

友元函数

介绍

C++中的友元函数是一种特殊的函数,它可以访问和操作类的私有成员和保护成员。友元函数可以在类的内部或外部声明和定义,但在其声明和定义中需要使用关键字 friend 来标识。友元函数可以是全局函数,也可以是其他类的成员函数。

下面是友元函数的一些重要特点和用法:

  1. 友元函数可以访问和操作类的私有成员和保护成员,这使得函数可以直接访问类的私有数据,而无需通过类的公有接口进行访问。

  2. 友元函数可以在类的内部或外部声明和定义。在类的内部声明友元函数时,使用friend关键字来标识函数为友元函数。在类的外部定义友元函数时,需要在函数的定义前面声明该函数为友元函数。

  3. 友元函数可以是全局函数,也可以是其他类的成员函数。如果友元函数是另一个类的成员函数,需要在该类的声明中将其声明为友元函数。

  4. 友元函数不是类的成员函数,因此它没有隐含的this指针,也无法访问类的非静态成员。

  5. 友元函数的声明可以放在类的任何位置,但其定义必须在类的外部。这是因为友元函数不属于类的成员,所以需要在类的外部定义。

  6. 友元函数可以用于重载操作符。这使得我们能够定义两个不同类对象之间的操作,例如添加两个自定义类的对象。

友元函数提供了一种特殊的访问和操作类的私有成员和保护成员的方式。它在某些情况下可以简化代码实现,但同时也破坏了封装性,因此需要谨慎使用。

创建友元函数

要创建一个友元函数,需要按照以下步骤进行操作:

1、在类内声明友元函数:在类的声明中,使用 friend 关键字来声明友元函数。友元函数可以是全局函数,也可以是其他类的成员函数。例如,下面的代码将一个全局函数声明为类的友元函数:

class MyClass {
public:
    // 声明友元函数
    friend void myFriendFunction();
};

2、在类的外部定义友元函数:在类的外部,需要定义先前声明的友元函数。这样,友元函数就可以访问和操作类的私有成员和保护成员。例如,下面的代码定义了先前声明的友元函数:

// 定义友元函数
void myFriendFunction() {
    // 在此函数中可以访问和操作MyClass的私有成员和保护成员
}

3、使用友元函数:使用类的对象或类名和作用域解析运算符(::)调用友元函数。例如,下面的代码演示了如何使用类对象调用友元函数:

MyClass obj;
myFriendFunction();  // 使用类对象调用友元函数

或者,如果友元函数是另一个类的成员函数,可以使用该类的对象或类名和作用域解析运算符(::)调用友元函数。例如,下面的代码演示了如何使用类对象调用另一个类的成员函数作为友元函数:

class MyClass2 {
public:
    // 声明友元函数
    friend void MyClass::myFriendFunction2();
};

// 定义友元函数
void MyClass::myFriendFunction2() {
    // 在此函数中可以访问和操作MyClass2的私有成员和保护成员
}

MyClass obj;
MyClass2 obj2;
obj.myFriendFunction2();  // 使用类对象调用友元函数

 运算符重载

运算符重载是指在类中重定义运算符的行为,使其适用于特定的类对象。运算符重载可以使得类对象的使用更加自然和方便,同时也提供了更多的灵活性。

要重载运算符,需要定义一个类成员函数或全局函数来执行所需操作,该函数的名称是运算符本身,并使用 operator 关键字声明。例如,要重载"+"运算符,可以定义一个名为operator+()的函数。

以下是一个示例,演示了如何重载"+"运算符:

class MyClass {
private:
    int value;
public:
    MyClass(int v) : value(v) {}

    MyClass operator+(const MyClass& other) {  // 运算符重载函数
        return MyClass(value + other.value);
    }

    int getValue() const {
        return value;
    }
};

int main() {
    MyClass obj1(3);
    MyClass obj2(5);

    MyClass result = obj1 + obj2;  // 使用重载的"+"运算符

    std::cout << result.getValue() << std::endl;  // 输出结果:8

    return 0;
}

上述示例中,我们在MyClass类中重载了"+"运算符。重载函数声明中的参数是另一个MyClass对象(const引用),并返回一个新的MyClass对象。在main函数中,我们创建了两个MyClass对象,然后使用重载的"+"运算符将它们相加,最终得到一个新的MyClass对象。

需要注意的是,在重载运算符时,可以根据需要选择将函数定义为类成员函数或全局函数(定义全局函数的时候需要将其声明为友元函数)。对于一些需要访问类私有成员的运算符,比如赋值运算符"=",通常选择将函数定义为类成员函数;对于一些不需要直接访问类私有成员的运算符,可以选择将函数定义为全局函数。

运算符重载可以大大简化代码,并提高可读性和可维护性。但是,需要谨慎使用运算符重载,以避免混淆和误用。同样,不应该过度使用运算符重载,以保持代码的简洁和易读性。

特殊运算符重载

1、赋值运算符重载
除了之前学习的无参构造函数、拷贝构造函数、析构函数之外,如果程序员不手写,编译器还会自动给类添加一个赋值运算符重载, 赋值运算符只能使用成员函数实现重载。 当类中出现指针类型的成员变量时,默认的赋值运算符重载函数类似于默认的浅拷贝构造函数,因此也需要手动编写解决浅拷贝的问题。

【常见面试题】一个类如果什么都不写,编译器添加了那些代码?

无参构造函数、拷贝构造函数、析构函数、赋值运算符重载函数。

2、类型转换运算符重载

类型转换运算符重载允许将一个类的对象转换为另一个类型。

类型转换运算符重载的语法如下:

operator type() {
    // 进行类型转换的操作
}

其中,type是要转换的目标类型。重载的类型转换运算符可以是类的成员函数或类的非成员函数。

下面是一个示例,演示了如何重载类型转换运算符:

class MyClass {
private:
    int value;
public:
    MyClass(int v) : value(v) {}

    operator int() {
        return value;
    }
};

int main() {
    MyClass obj(42);

    int result = obj;  // 使用重载的类型转换运算符将MyClass对象转换为int

    std::cout << result << std::endl;  // 输出结果:42

    return 0;
}

在上述示例中,我们定义了一个名为MyClass的类,并在其中重载了类型转换运算符。重载函数声明中的返回类型是要转换的目标类型(int),并在函数体中返回了MyClass对象的value成员。

在main函数中,我们创建了一个MyClass对象,并将其赋值给一个int类型的变量。由于我们已经重载了类型转换运算符,所以可以直接将MyClass对象转换为int类型。最终,输出结果为42。

需要注意的是,在重载类型转换运算符时,应该在函数体中进行适当的类型转换操作,并返回目标类型的对象。此外,需要慎谨使用类型转换运算符,以避免混淆和误用。

另外,C++还提供了显式转换运算符重载(explicit conversion operator),通过在重载函数前加上 explicit 关键字来实现。这样,在使用显式转换运算符进行类型转换时,需要明确使用转换运算符进行转换。

注意事项

● 重载的运算符限制在C++语言中已有的运算符范围,不能创建新的运算符。

● 运算符重载本质上也是函数重载,但是不支持函数参数默认值。

● 重载之后的运算符不能改变运算符的优先级和结合性。也不能改变运算符的操作数和语法结构。

● 运算符重载必须基于或包含自定义类型,不能改变基本数据类型的运算规则。

● 重载的功能应该与原有的功能类似,避免没有目的的滥用运算符重载。

● 一般情况下,双目运算符建议使用友元函数运算符重载,单目运算符建议使用成员函数运算符重载。

标签:友元,函数,成员,C++,运算符,重载,MyClass
From: https://blog.csdn.net/a1547998353/article/details/139827233

相关文章

  • c++ 多重包含/定义 || 链接性 || 生命周期
     作用域&&生命周期C++中的作用域(scope)指的是变量、函数或其他标识符的可见和可访问的范围。生命周期(Lifetime)指的是变量或对象存在的时间段。它开始于变量或对象的创建(定义)时刻,结束于其被销毁的时刻。作用域:通过其声明的位置来确定。全局作用域:定义在(类/函数)外部......
  • c++ virtual || virtual =0
    虚函数&&纯虚抽象类:包含纯虚函数的类称为抽象类,继承层次结构的较上层。作用:将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。继承:子类继承基类的成员及成员函数,不可以删除,可以(修改)通过虚函数重写......
  • 【C++】list的使用方法和模拟实现
    ❤️欢迎来到我的博客❤️前言list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素list与forward_list非常相似:最......
  • 2024年华为OD机试真题-分披萨-(C++/Java/python)-OD统一考试(C卷D卷)
    题目描述"吃货"和"馋嘴"两人到披萨店点了一份铁盘(圆形)披萨,并嘱咐店员将披萨按放射状切成大小相同的偶数个小块。但是粗心的服务员将披萨切成了每块大小都完全不同奇数块,且肉眼能分辨出大小。由于两人都想吃到最多的披萨,他们商量了一个他们认为公平的分法:从"吃货"开始,轮流......
  • c/c++ 数据结构 顺序栈
    本文是以c语言的风格编写的c++程序。栈的特点:先进后出,后进先出。顺序栈的结构定义:一个数组以及一个”指针“(不是真正的指针,而是位置变化的说明)#include<stdio.h>#include<malloc.h>#defineMaxsize20typedefstruct{ intdata[Maxsize]; inttop;}SqStack; 初......
  • 2020C++等级考试二级真题题解
     202012数组指定部分逆序重放c++ #include<iostream>usingnamespacestd;intmain(){  inta[110];  intn,k;  cin>>n>>k;  for(inti=0;i<n;i++){    cin>>a[i];  }  for(inti=0;i<k/2;i++){......
  • C++系统相关操作1 - 调用命令行并获取返回值
    1.关键词2.sysutil.h3.sysutil.cpp3.1.system_util_unix.cpp3.2.system_util_win.cpp4.测试代码5.运行结果6.源码地址1.关键词关键词:C++系统调用systempopen跨平台应用场景:希望直接调用操作系统的某些命令,并获取命令的返回值。2.sysutil.h#pragm......
  • C++系统相关操作2 - 获取系统环境变量
    1.关键词2.sysutil.h3.sysutil.cpp4.测试代码5.运行结果6.源码地址1.关键词C++系统调用环境变量getenv跨平台2.sysutil.h#pragmaonce#include<cstdint>#include<string>namespacecutl{/***@briefGetanenvironmentvariable.......
  • C++核心编程运算符的重载
    C++核心编程运算符的重载文章目录C++核心编程运算符的重载1.“+”运算符的重载1.1作为成员函数重载1.2作为全局函数重载2."<<"运算符重载2.1为什么需要重载左移运算符2.2如何重载左移运算符2.3注意事项3."++"运算符重载3.1前置递增运算符重载3.2后置递增运算符重载......
  • 2022年大作业参考报告-使用C++语言开发小学生成绩管理系统、中学生成绩管理系统、大学
    背景:目录第一章需求分析   21.1   问题描述   26.1   功能需求   26.2   开发环境   26.3   开发过程   2第二章概要设计   32.1   总体设计   32.2   类的定义   32.3   接口设计   52.4  ......