c++学习(day4)

这篇具有很好参考价值的文章主要介绍了c++学习(day4)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一. 友元(friend)

友元是一种定义在类外部的普通函数或类

1 友元函数

1.1 全局函数作为友元函数

声明一个全局函数作为类的友元函数,则允许该全局函数,访问类中各个权限下的成员

在类中要将该函数进行声明:friend 全局函数头;

#include <iostream>
using namespace std;

class Stu
{
private:
    string name;
    int age;
public:
    Stu() {}
    Stu(string n, int a) : name(n), age(a) {}

    void show()
    {
        cout << "name = " << name << "   age = " << age << endl;
    }

    friend void display(Stu s); // 在类中将全局函数声明成类的友元函数
};

// 定义全局函数
void display(Stu s)
{
    cout << "name = " << s.name << "   age = " << s.age << endl; // 正常的全局函数不能访问类中私有属性
}

int main()
{
    display(Stu("zhangpp", 18));

    return 0;
}

1.2 类的成员函数作为友元函数(了解)

  1. 声明一个其他类的成员函数作为自己类的友元函数,则允许让该函数访问自己类中的所有成员

  2. 要求该函数必须类内声明类外定义

2. 友元类

  1. 在一个类中声明另一个类当做友元类,则允许友元类中所有成员访问自己的所有权限下的成员

  2. 声明格式:friend class 类名;

#include <iostream>

using namespace std;

class Stu; // 类的前置声明

class Teacher // 老师类
{
private:
    string sub;

public:
    Teacher() {}
    Teacher(string s) : sub(s) {}

    void show(Stu s); // 类内声明
};

class Stu // 学生类
{
private:
    string name;
    int age;

public:
    Stu() {}
    Stu(string n, int a) : name(n), age(a) {}

    void show()
    {
        cout << "name = " << name << "   age = " << age << endl;
    }

    friend void display(Stu s); // 在类中将全局函数声明成类的友元函数

    // friend void Teacher::show(Stu s);             //声明老师类中的成员函数作为友元函数

    // 将整个Teacher类当做友元类
    friend class Teacher;
};

// 定义全局函数
void display(Stu s)
{
    cout << "name = " << s.name << "   age = " << s.age << endl; // 正常的全局函数不能访问类中私有属性
}

// 老师类中的成员函数的定义
void Teacher::show(Stu s)
{
    cout << "Teacher::sub = " << sub << endl;
    cout << "Stu::name = " << s.name << endl; // 其他类不能访问类中私有成员
}

int main()
{
    display(Stu("zhangpp", 18));
    Teacher t1("C++");
    t1.show(Stu("zhangpp", 18));
    return 0;
}

3. 使用友元的注意事项

  1. 友元具有方向性,A把B当做朋友,允许B访问A的所有权限,但是A不一定能访问B的所有权限

  2. 友元不具有交换性:A是B的朋友,但B不一定是A的朋友

  3. 友元不具有传递性:A是B的朋友,B是C的朋友,则A不一定是C的朋友

  4. 友元不具有继承性:父类的朋友不一定是子类的朋友

  5. 由于友元的出现,破坏了了类的封装性,使得访问权限形同虚设。所以不在万不得已的情况下,尽可能少用友元

  6. 必须使用友元的情况:插入和提取运算符重载。(后期讲)

二. 常成员函数和常对象(const)

1. 常成员函数

由const修饰的成员函数叫常成员函数

格式:

  • 返回类型 成员函数名(参数表) const;

    例如:int function(int x) const

特点:

  1. 在常成员函数中,不能修改成员变量的值(保护成员变量不被修改)

  2. 类中同名的常成员函数和非常成员函数构成重载关系,原因是隐藏的this指针的类型不同

    非常成员函数中: Stu * const  this
    常成员函数中:	  const Stu * const this
    
  3. 非常对象,优先调用非常成员函数,如果没有非常成员函数,则去调用同名的常成员函数

  4. 常对象只能调用常成员函数,没有常成员函数会报错

2. 常对象

const修饰的对象为常对象

  1. 常对象只能调用常成员函数,不能调用非常成员函数

  2. 当一个常引用的目标为非常对象,则通过引用只能调用常成员函数,通过对象优先调用非常成员函数

3. mutable关键字

功能:

取消成员常属性

使用方式:

​ 定义变量前加mutable,那么,该变量就能在常成员函数中被修改

#include <iostream>

using namespace std;

class Stu
{
private:
    string name;
    mutable int age; // 由mutable关键字修饰的成员变量,能在常成员函数中被修改
    double score;

public:
    Stu() {}
    Stu(string n, int a, double s) : name(n), age(a), score(s) {}

    // 有const修饰的成员函数就是常成员函数
    void show() const // const Stu * const this;
    {
        // this = nullptr;
        // this->score = 50; // 在常成员函数中不能修改成员变量的值
        this->age = 100; // 可以更改,因为有关键字修饰
        cout << "name = " << name << endl;
        cout << "age = " << age << endl;
        cout << "score = " << score << endl;
        cout << "AAAAAAAAAAAAAAAAAAAAAA" << endl;
    }

    void show() // Stu * const this;
    {
        this->score = 50; // 在常成员函数中不能修改成员变量的值
        cout << "name = " << name << endl;
        cout << "age = " << age << endl;
        cout << "score = " << score << endl;
        cout << "BBBBBBBBBBBBBBBBBBBBBBBB" << endl;
    }
};

int main()
{
    Stu s1("张三", 20, 99);
    s1.show(); // 50

    const Stu &r = s1;

    r.show();  // 常成员函数
    s1.show(); // 优先调用非常成员函数

    return 0;
}

三. 运算符重载

单、算、关、逻、条、赋、逗

c++学习(day4)

1. 定义

所谓运算符重载,就是给运算符新的含义,能够实现“一符多用”,也是属于静态多态的一种,

他能够实现将原本加载到基本数据类型的运算符,在自定义类对象减使用。

好处:能够使得代码更加简洁、易懂,优雅好看

2. 重载的方法

统一的名称:

operator# //#表示运算符

3. 运算符重载要求

功能:

​ 实现运算符对应的操作

参数:

​ 由运算符本身决定

返回值:

​ 由用户自己决定

4. 调用时机及调用原则

调用时机:当使用该运算符时,系统自动调用,无需手动调用

调用原则:左调右参 //a = b; a.operator=(b)

5. 运算符重载的格式

每种运算符都有两个版本的格式:

  • 成员函数版本,类对象本身就是一个参数,形参个数是操作数个数-1

  • 全局函数版,需要使用友元完成,此时参数个数等于操作数个数

以上两个版本的重载函数,只能实现一个,否则调用时会混乱报错

5.1 算术类运算符重载(双目运算符)

种类:

​ +、-、*、/、%。。。

表达式:

​ L#R //L表示左操作数,#表示运算符,R表示右操作数

  • 左操作数:既可以是左值也可以是右值

  • 右操作数:既可以是左值也可以是右值

  • 结果:右值

定义格式:

  • 成员函数版:const 类名 operator#( const 类名 &R ) const

第一个const:保护返回结果不被改变

第二个const:保护右操作数不被修改

第三个const:保护左操作数不被修改

  • 全局函数版:const 类名 operator#(const 类名 &L, const 类名 &R)

5.2 赋值类运算符重载

种类:

​ =、-=、*=、/=、%=、+=。。。

表达式:

​ L#R //L表示左操作数,#表示运算符,R表示右操作数

  • 左操作数:只能是左值

  • 右操作数:既可以是左值也可以是右值

  • 结果:右值

定义格式:

  • 成员函数版: 类名 & operator#( const 类名 &R )

  • 全局函数版: 类名 & operator#(类名 &L, const 类名 &R)

5.3 关系运算符重载

种类:

​ >=、<=、!=、>、<、==。。。

表达式:

​ L#R //L表示左操作数,#表示运算符,R表示右操作数

  • 左操作数:既可以是左值也可以是右值

  • 右操作数:既可以是左值也可以是右值

  • 结果:bool类型的右值

定义格式:

  • 成员函数版: const bool operator#( const 类名 &R ) const

  • ==全局函数版: const bool operator#( const 类名 &L,const 类名 &R ) ==

5.4 单目运算符重载

种类:

​ &、!、-(负号)。。。

表达式:

​ #O //#表示运算符 O表示操作数

  • 操作数:既可以是左值也可以是右值

  • 结果:右值

定义格式:

  • 成员函数版: const 类名 operator#( void ) const

  • 全局函数版: const 类名 operator#( conts 类名 &O)

5.4 单目运算符重载

种类:

​ &、!、-(负号)。。。

表达式:

​ #O //#表示运算符 O表示操作数

  • 操作数:既可以是左值也可以是右值

  • 结果:右值

定义格式:

  • 成员函数版: const 类名 operator#( void ) const

  • 全局函数版: const 类名 operator#( conts 类名 &O)

5.5 自增、自减运算符重载

前置自增:++a

表达式:

​ #O //#表示运算符 O表示操作数

  • 操作数:只能是左值

  • 结果:左值,自身的引用

定义格式:

  • 成员函数版: 类名 & operator#( )

  • 全局函数版: 类名 & operator#( 类名 &O )

后置自增:a++

表达式:

​ O# //#表示运算符 O表示操作数

  • 操作数:只能是左值

  • 结果:右值

定义格式:

  • 成员函数版: 类名 operator#( int )

  • 全局函数版: 类名 operator#( 类名 &O , int)

5.6 插入、提取运算符重载

  1. cin和cout的来源
namespace std
{
    extern istream cin;         /// Linked to standard input
    extern ostream cout;        /// Linked to standard output
}
  1. 从这两个类对象可以看出,cin和cout来自于内置对象,想要对<<和>>进行重载时,如果想要实现成员函数版,则需要多istream和ostream类进行修改,难度较大。

  2. 此时,我们可以使用全局函数版实现这两个运算符的重载,将该全局函数设置成友元函数即可

  3. 因为要用到istream和ostream的类对象和自定义类对象,原则上来说需要在两个类中都要设置成友元函数

  4. 但是,在运算符重载函数中,只会对自定义的类对象访问私有成员,而不会对istream和ostream的类访问私有成员

  5. 所以,最终只需要在自定义类中,将全局函数设置成友元函数即可

表达式:

​ L#R (L是cin或cout #是<<或者>> R是自定义的类对象)

  • 左操作数:istream和ostream的类对象

  • 右操作数:自定义的类对象

  • 结果:左操作数自身的引用

格式:

  • ostream &operator<<(ostream &L, const 类名 &O);

  • istream &operator>>(istream &L, const 类名 &O);

5.6 不能重载的运算符

  1. 成员运算符 .

  2. 成员指针运算符 .*

  3. 条件表达式 ?:

  4. 求字节运算 sizeof

  5. 作用域限定符 ::

5.7 运算符重载注意事项

  1. 运算符重载只能在已有的运算符基础上进行重载,不能凭空捏造一个运算符

  2. 运算符重载不能更改运算符的本质:如不能在加法运算符重载中实现减法

  3. 运算符重载不能改变运算符的优先级

  4. 运算符重载不能改变运算符的结合律

  5. 运算符重载函数不能设置默认参数

​ L#R (L是cin或cout #是<<或者>> R是自定义的类对象)

  • 左操作数:istream和ostream的类对象

  • 右操作数:自定义的类对象

  • 结果:左操作数自身的引用

格式:

  • ostream &operator<<(ostream &L, const 类名 &O);

  • istream &operator>>(istream &L, const 类名 &O);

5.6 不能重载的运算符

  1. 成员运算符 .

  2. 成员指针运算符 .*

  3. 条件表达式 ?:

  4. 求字节运算 sizeof

  5. 作用域限定符 ::

5.7 运算符重载注意事项

  1. 运算符重载只能在已有的运算符基础上进行重载,不能凭空捏造一个运算符

  2. 运算符重载不能更改运算符的本质:如不能在加法运算符重载中实现减法

  3. 运算符重载不能改变运算符的优先级

  4. 运算符重载不能改变运算符的结合律

  5. 运算符重载函数不能设置默认参数

  6. 成员函数版的参数要比全局函数版的参数少一个,原因是对象本身就是一个参数文章来源地址https://www.toymoban.com/news/detail-422491.html

到了这里,关于c++学习(day4)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端学习——JS进阶 (Day4)

    练习 throw 抛异常 try/catch 捕获错误信息 debugger this指向——普通函数 改变this 节流 案例 防抖

    2024年02月16日
    浏览(43)
  • day4 驱动开发 c语言学习

    不利用系统提供的register_chrdev,自己实现字符设备的注册 底层代码 led.c 应用层代码 app.c 头文件 head.h

    2024年02月14日
    浏览(39)
  • 【C++】继承的基本特性(定义,赋值转换,友元,静态成员,虚拟继承,默认成员函数,作用域)

    🌏博客主页: 主页 🔖系列专栏: C++ ❤️感谢大家点赞👍收藏⭐评论✍️ 😍期待与大家一起进步! 它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。 Person是父类,也称作基类。Student是子类,也称作派生类 总结: 基类private成员

    2024年02月14日
    浏览(47)
  • 【Node.js学习 day4——模块化】

    什么是模块化与模块? 将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化 其中拆分的 每个文件就是一个模块 ,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用。 什么是模块化项目? 编码时是按照模块一个一个编码的

    2024年01月16日
    浏览(49)
  • 数据结构与算法学习(day4)——解决实际问题

    在本章的学习此前,需要复习前三章的内容,每个算法都动手敲一遍解题。宁愿学慢一点,也要对每个算法掌握基本的理解! 前面我们学习了简化版桶排序、冒泡排序和快速排序三种算法,今天我们来实践一下前面的三种算法。 本章的学习目标: (1)回顾三个算法的基本原

    2024年02月09日
    浏览(54)
  • 数学建模学习笔记day4——层次化分析

    层次分析主要有三大典型应用 (1)用于最佳方案的选取 (2)用于评价类问题 (3)用于指标体系的优选 层次分析法是根据问题的性质和要达成的目标,将问题分解为不同的组成因素,将因素按不同层次聚集组合,形成一个多层次的分析结构模型,最终问题归结为最低层(决

    2024年02月09日
    浏览(42)
  • 初识C语言——详细入门(系统性学习day4)

    目录 前言 一、C语言简单介绍、特点、基本构成 简单介绍: 特点: 基本构成: 二、认识C语言程序 标准格式: 简单C程序: 三、基本构成分类详细介绍    (1) (2)数据类型 计算机中常用存储单位 数据类型的取值范围 打印输入类型 (3)常量和变量 常量的分类:

    2024年02月08日
    浏览(37)
  • Vue3学习日记 Day4 —— pnpm,Eslint

    注:此课程需要有Git的基础才能学习 1、使用原因     1.1、速度快,远胜过yarn和npm     1.2、节省磁盘空间   2、使用方式     2.1、安装方式         npm install -g pnpm     2.2、创建项目         pnpm create vue     1、禁用Prettier插件(如果安装了) 2、安装Eslint插件,并配置保存

    2024年04月12日
    浏览(34)
  • C++学习笔记——友元、嵌套类、异常

    目录 一、友元 一个使用友元的示例代码 输出结果 二、嵌套类 一个使用嵌套类的示例代码 输出结果 三、异常 一个使用异常处理的示例代码 输出结果 四、结论 五、使用它们的注意事项 上一篇文章链接: C++中的继承和模板是非常强大和灵活的特性,它们可以帮助我们实现代

    2024年02月02日
    浏览(109)
  • Go语言学习查缺补漏ing Day4

    Go语言学习查缺补漏ing Day4 一、掌握iota的使用 请看下面这段代码: 思考一下输出结果会是什么? 先不公布输出结果。先来谈一谈iota,iota是用于给需要自增长常量赋值的标识符。我们可以用下划线 _ 来省略掉不想要的值。而我们中间给变量赋值了,然后如果后续的变量如果

    2024年02月15日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包