当前位置: 首页 > article >正文

嵌入式学习-C嘎嘎-Day03

嵌入式学习-C嘎嘎-Day03

1. 友元 friend

1.1 概念

1.2 友元函数

1.3 友元类

1.4 友元成员函数

2. 运算符重载

2.1 概念

2.2 友元函数运算符重载

2.3 成员函数运算符重载

2.4 特殊运算符重载

2.4.1 赋值运算符重载

2.4.2 类型转换运算符重载

2.5 注意事项

3. 字符串类型 string

1. 友元 friend

1.1 概念

定义:

类实现了数据的隐藏与封装,类的成员变量一般定义为私有成员,仅能通过类的成员函数才能读写。如果成员变量定义为公共的,则又破坏了封装性。但是某些情况下,需要频繁读写类的成员变量,特别是在对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。

友元是一种定义在类外部的普通函数,但他需要在类体内进行说明,为了和该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数但是他能够访问类中的私有成员

作用:

在于提高程序的运行效率,但是,他破坏了类的封装性和隐藏性,使得非成员函数能够访问类的私有成员。导致程序维护性变差,因此使用友元要慎用

友元主要的应用场景是运算符重载

用法:

  • 友元函数
  • 友元类
  • 友元成员函数

1.2 友元函数

友元函数是一种在类内说明,但本身属于类外的函数,可以访问类内所有成员。

#include <iostream>

using namespace std;

class Girl
{
private:
    int age;

public:
    Girl(int age):age(age){}

    int get_age() const
    {
        return 18;
    }

    // 友元关系说明
    friend void get_real_age(Girl& g);
};

void get_real_age(Girl& g)
{
    // 获取私有成员的数值并修改
    cout << "真实年龄:" << g.age << endl;
    g.age = 18;
    cout << "修改后的真实年龄:" << g.age << endl;
}

int main()
{
    Girl g(40);
    cout << g.get_age() << endl; // 18
    get_real_age(g);
}

需要注意的是:

  • 友元函数没有this指针
  • 友元函数的类内说明可以在任何部分,不受权限的影响
  • 一个函数可以是多个类的友元函数,只需要分别在个各类中说明

1.3 友元类

当一个类B成为了另一个类A的友元类时,类A的所有成员就可以被类B访问了。

#include <iostream>

using namespace std;

class A
{
private:
    int value  = 1;

    // 友元关系说明
    friend class B;
};

class B
{
public:
    void access(A& a)
    {
        cout << this << endl;
//        cout << this->value << endl;  错误
        // 读取和修改a的private成员
        cout << a.value++ << endl;
        cout << a.value << endl;
    }
};

int main()
{
    A a;
    B b;
    b.access(a);
}

需要注意的是:

  • 友元关系不能被继承
  • 友元关系不具有交换性
  • 友元关系不具有传递性

1.4 友元成员函数

#include <iostream>

using namespace std;

// 3. 上一步用到了A类,补充A声明
class A;

// 2. 上一步中用到了B类,补充B类
class B
{
public:
    void access(A& a);
};


class A
{
private:
    int value  = 1;

    // 1. 说明友元关系
    friend void B::access(A& a);
};

// 4. 补充友元成员函数的内容
void B::access(A &a)
{
    cout << a.value++ << endl;
    cout << a.value << endl;
}

int main()
{
    A a;
    B b;
    b.access(a);
}

2. 运算符重载

2.1 概念

函数可以重载,在C++中运算符也可以重载,运算符默认的操作类型只能是基本数据类型,但是对于很多用户自定义类型,也需要类似的运算操作,此时可以重载运算符,赋予这些运算符新的功能,执行对于自定义类型的运算操作。

可以被重载的运算符:

算术运算符:+、-、*、/、%、++、--

位操作运算符:&、|、~、^(位异或)、<<(左移)、>>(右移)

逻辑运算符:!、&&、||

比较运算符:<、>、>=、<=、==、!=

赋值运算符:=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=

其他运算符:[]、()、->、,、new、delete、new[]、delete[]

不被重载的运算符:

成员运算符“.”、指针运算符“*”、三目运算符“? :”、sizeof、作用域“::”

通常有两种运算符重载的方式(部分运算符只支持一种):

  • 友元函数运算符重载
  • 成员函数运算符重载

可以把运算符重载看做是一种特殊的函数重载,运算符是一种特殊的函数。

2.2 友元函数运算符重载

#include <iostream>

using namespace std;

/**
 * @brief The Integer class 整数类
 */
class Integer
{
private:
    int value;

public:
    Integer(int v):value(v){}

    int get_value() const
    {
        return value;
    }

    friend Integer operator +(const Integer& i1,const Integer& i2);
    friend Integer operator ++(Integer& i); // 前置
    friend Integer operator ++(Integer& i,int); // 后置
};

Integer operator +(const Integer& i1,const Integer& i2)
{
    return i1.value + i2.value;
}

Integer operator ++(Integer& i)
{
    return ++i.value;
}

Integer operator ++(Integer& i,int)
{
    return i.value++;
}

int main() {
    Integer i1(1);
    Integer i2(2);
    Integer i3 = i1+i2;
    cout << i3.get_value() << endl; // 3
    cout << (++i3).get_value() << endl; // 4
    cout << (i3++).get_value() << endl; // 4
    cout << i3.get_value() << endl; // 5

    return 0;
}

2.3 成员函数运算符重载

成员函数运算符重载与友元函数运算符重载的最大区别是,成员函数运算符重载的输入参数比友元函数运算符重载少一个,因为友元函数的第一个参数在成员函数中使用this指针表示。

#include <iostream>

using namespace std;

/**
 * @brief The Integer class 整数类
 */
class Integer
{
private:
    int value;

public:
    Integer(int v):value(v){}

    int get_value() const
    {
        return value;
    }

    Integer operator +(const Integer& i);
    Integer operator ++(); // 前置
    Integer operator ++(int); // 后置
};

Integer Integer::operator +(const Integer& i)
{
    // this指针可以不写,因为只要不重名编译器自动添加
    return this->value + i.value;
}

Integer Integer::operator ++()
{
    return ++this->value;
}

Integer Integer::operator ++(int)
{
    return this->value++;
}

int main() {
    Integer i1(1);
    Integer i2(2);
    Integer i3 = i1+i2;
    cout << i3.get_value() << endl; // 3
    cout << (++i3).get_value() << endl; // 4
    cout << (i3++).get_value() << endl; // 4
    cout << i3.get_value() << endl; // 5

    return 0;
}

2.4 特殊运算符重载

2.4.1 赋值运算符重载

如果程序员不手动在类中编写赋值运算符重载函数,编译器会为这个类自动添加一个默认的赋值运算符重载函数,以便于实现同类型对象的赋值操作。

赋值运算符重载函数只能通过成员函数实现,因为需要this指针。

需要注意的是,如果出现了浅拷贝,也要修改赋值运算符重载函数的逻辑。

#include <iostream>

using namespace std;

/**
 * @brief The Integer class 整数类
 */
class Integer
{
private:
    int value;

public:
    Integer(int v):value(v){}

    int get_value() const
    {
        return value;
    }

    // 默认的赋值运算符重载函数
    Integer& operator =(const Integer& i)
    {
        this->value = i.value;
        return *this;
    }
};


int main() {
    Integer i1(1);
    Integer i2(2);
    i1 = i2; // 赋值运算
    cout << i1.get_value() << endl; // 2

    return 0;
}

【思考】截止到目前,如果一个成员手写了一个空类,编译器会自动添加哪些内容:

  • 构造函数
  • 析构函数
  • 拷贝构造函数
  • 赋值运算符重载函数

2.4.2 类型转换运算符重载

类型转换运算符重载函数也必须使用成员函数重载,格式比较特殊

#include <iostream>

using namespace std;

/**
 * @brief The Integer class 整数类
 */
class Integer
{
private:
    int value;

public:
    Integer(int v):value(v){}

    int get_value() const
    {
        return value;
    }

    // 类型转换函数
    operator int()
    {
        return value;
    }
};


int main() {
    // int → Integer
    Integer i1 = 1; // 编译器自动调用构造函数
    cout << i1.get_value() << endl; // 1

    // Integer → int
    int i2 = i1;
    cout << i2 << endl; // 1

    return 0;
}

2.5 注意事项

  • 运算符重载只能限制在C++已有的范围内,不能创建的新的运算符。
  • 运算符重载不能改变运算符的优先级和结合性。
  • 运算符重载不能改变运算符的操作数和语法结构。
  • 运算符重载的操作数一定包含自定义类型。
  • 运算符重载的功能应该与原有功能相似,避免滥用。
  • 运算符重载不能设置参数默认值。
  • 通常单目运算符优先考虑成员函数,双目运算符优先考虑友元函数。

3. 字符串类型 string

之前对string的学习比较基础,实际上string有诸多字符串处理函数。

#include <iostream>
#include <string.h>

using namespace std;


int main() {
    string s; // 创建一个空字符串
    cout << s.empty() << endl; // 判断内容是否为空
    // 构造函数,参数const char*
    string s1 = "Thursday";
    string s2("Thursday");
    cout << (s1 == s2) << endl;
    // 拷贝构造函数
    string s3 = s1;
    string s4(s3);
    cout << s3 << " " << s4 << endl;

    // 参数1:const char* 原字符串
    // 参数2:保留前几个字符
    string s5("ABCDEFG",2);
    cout << s5 << endl; // AB

    s = "ABCDEFG";
    // 参数1:string 原字符串
    // 参数2:不保留前几个字符
    string s6(s,2);
    cout << s6 << endl; // CDEFG

    // 参数1:数量
    // 参数2:字符元素
    string s7(5,'A');
    cout << s7 << endl; // AAAAA

    swap(s6,s7); // 交换
    cout << s6 << endl;
    cout << s7 << endl;

    // 连接
    cout << s6 + s7 << endl;

    // 尾插单字符
    s6.push_back('B');
    cout << s6 << endl;

    // 尾插string,支持链式调用
    s6.append("CC").append("DDD");
    cout << s6 << endl;

    // 在第二个位置插入内容"111"
    s6.insert(1,"111");
    cout << s6 << endl;

    // 参数1:替换的起始位置
    // 参数2:替换的字符数
    // 参数3:替换的新内容
    s6.replace(0,3,"*****");
    cout << s6 << endl;

    s6.pop_back(); // 删除最后一个字符
    cout << s6 << endl;

    s6 = "1234567890";
    // 参数1:删除的起始位置
    // 参数2:删除的字符数
    s6.erase(3,4);
    cout << s6 << endl;

    s6.clear(); // 清空
    cout << s6.size() << endl; // 0

    s6 = "ABCDEFGHIJK";
    char c[20];
    // 参数1:拷贝的目标
    // 参数2:拷贝的字符数量
    // 参数3:拷贝的起始位置
    s6.copy(c,5,2);
    cout << c << endl;

    // C++ string→C string
    strcpy(c,s6.c_str());
    cout << c << endl;

    return 0;
}


http://www.kler.cn/a/403204.html

相关文章:

  • 网络安全-企业环境渗透2-wordpress任意文件读FFmpeg任意文件读
  • Redis密码设置与访问限制(网络安全)
  • 使用docker快速部署Nginx、Redis、MySQL、Tomcat以及制作镜像
  • 游戏行业趋势:“AI、出海、IP”大热下,如何提升竞争力?
  • uniapp开发微信小程序笔记3-全局配置、导航栏配置、tabBar配置
  • [Realtek sdk-3.4.14b] RTL8197FH-VG新增jffs2分区操作说明
  • 单片机学习笔记 1. 点亮一个LED灯
  • 创建型设计模式(模版方法、观察者模式、策略模式)
  • 网络安全实施方案
  • 关联度分析、灰色预测GM(1,1)、GM(1,1)残差模型——基于Python实现
  • 类和对象——static 成员,匿名对象(C++)
  • OAI-5G开源通信平台实践(三)
  • linux 软连接的使用
  • tensorflow有哪些具体影响,和chatgpt有什么关系
  • [Unity]【游戏相关】 游戏设计基础:如何创建有效的游戏设计文档
  • C++常用库
  • Git错误:gnutls_handshake() failed: The TLS connection was non-properly terminated
  • mybatis的动态sql用法之排序
  • 同三维T80003JEHS 4K/60帧HDMI/SDI超高清H.265解码器
  • java arr.length 获取数组长度 开销 详解
  • x的算术平方根( 二分查找)
  • SQL Server Management Studio 的JDBC驱动程序和IDEA 连接
  • 跨平台WPF框架Avalonia教程 十二
  • 关于安卓模拟器或手机设置了BurpSuite代理和安装证书后仍然抓取不到APP数据包的解决办法
  • 基于gradio+networkx库对图结构进行可视化展示
  • TypeScript 与 JavaScript 的主要区别及使用场景