2023/9/11 -- C++/QT

2023-09-11 20:41:22

作业

仿照string类,完成myString 类

02mystring.h:

#ifndef __02MYSTRING_H__
#define __02MYSTRING_H__

#include <iostream>
#include <cstring>

using namespace std;

class myString{
private:
    char *str;
    int size;
public:
    //无参构造
    myString();
    //有参构造
    myString(const char *s);
    //拷贝构造
    myString(const myString &other);
    //析构函数
    ~myString();
    //拷贝赋值函数
    myString & operator=(const myString &other);
    //判空
    bool empty()const;
    //size
    size_t string_size()const;
    //c_str
    const char *c_str()const;
    //at
    char &at(int pos);
    //+运算符
    const myString operator+(const myString &s)const;
    //+=运算符
    myString &operator+=(const myString &s);
    //>运算符
    bool operator>(const myString &s)const;
    //[]运算符
    char operator[](int index);
};

#endif // 02MYSTRING_H

 02mystring.cpp:

#include <02mystring.h>
//无参构造
myString::myString():size(10){
    str = new char[size];
    strcpy(str,"");
    //cout<<"无参构造函数"<<endl;
}
//有参构造
myString::myString(const char *s){
    size = strlen(s);
    str = new char[size+1];
    strcpy(str,s);
    //cout<<"有参构造函数"<<endl;
}
//拷贝构造
myString::myString(const myString &other):size(other.size){
    str = new char[other.size+1];
    strcpy(str,other.str);
    //cout<<"拷贝构造函数"<<endl;
}
//析构函数
myString::~myString(){
    delete []str;
    str = nullptr;
    //cout<<"析构函数"<<endl;
}
//拷贝赋值函数
myString & myString::operator=(const myString &other){
    size = other.size;
    strcpy(str,other.str);
    //cout<<"拷贝赋值函数"<<endl;
    return *this;
}
//判空
bool myString::empty()const{
    return size == 0;
}
//size
size_t myString::string_size()const{
    return size;
}
//c_str
const char *myString::c_str()const{
    return str;
}
//at
char &myString::at(int pos){
    if(pos < 0 || pos >= size){
        cout<<"pos出错啦!"<<endl;
        return *str;             //不知道返回值些什么...嘻嘻,就返回第一个位置...
    }
    return *(str+pos);
}
//+运算符
const myString myString::operator+(const myString &s)const{
    myString S;
    delete []S.str;
    S.size = size + s.size;
    S.str = new char[S.size+1];
    strcpy(S.str,str);
    strcat(S.str,s.str);
    return S;
}
//+=运算符
myString &myString::operator+=(const myString &s){
    size += s.size;
    strcat(str,s.str);
    return *this;
}
//>运算符
bool myString::operator>(const myString &s)const{
    return strcmp(str,s.str) > 0;
}
//[]运算符
char myString::operator[](int index){
    return *(str+index);
}

main.cpp:

#include <02mystring.h>

int main()
{
    myString s1("hello");
    myString s2("world");
    myString s5;
    cout<<"s1.size = "<<s1.string_size()<<endl;
    cout<<"c_str(s2) = "<<s2.c_str()<<endl;
    myString s3 = s1 + s2;
    cout<<"s3.at(5) = "<<s3.at(5)<<endl;
    myString s4("nihao");
    s4 += s2;
    if(s4 > s3){
        cout<<"s4 > s3"<<endl;
    }else {
        cout<<"s4 <= s3"<<endl;
    }
    cout<<"s4[4] = "<<s4[4]<<endl;
    return 0;
}

 效果图:

一、友元(friend)

1.1 友元函数

1> 普通的类的非成员函数,是无权访问类中的受保护成员以及私有成员的,但是,将一个函数设置成友元函数,那么该函数可以访问该类中的所有权限下的成员,

2> 友元函数分为全局函数作为友元函数和其他类的成员函数作为友元函数

3> 全局函数作为友元函数格式:在类内进行全局函数的声明:friend 函数头;

4> 其他类的成员函数作为友元函数,声明格式:friend 其他类::函数名(形参列表);

1.2 友元类

将一个类声明成友元类,那么就运行这个类中的所有成员函数来访问自己类中的私有成员了

#include <iostream>

using namespace std;
class Stu;             //对类的前置声明

void fun(Stu &s);            //将函数进行前置声明

class Teacher
{
private:
    string name;
    string subject;          //科目

public:
    Teacher() {}
    Teacher(string n, string s):name(n), subject(s) {}

    void display(Stu &s);             //类内声明,类外定义

};

class Stu
{
private:
    string name;
    int age;
    double score;

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

    //类中的成员函数,可以直接使用所有权限下的成员
    void show()
    {
        cout<<"name = "<<name<<"  age = "<<age<<"  score = "<<score<<endl;
    }

    //将全局函数声明成友元函数,那么该全局函数可以访问该类中的所有权限下的成员
    friend void fun(Stu &s);

    //将老师类中的成员函数设置成友元函数
    //friend void Teacher::display(Stu &s);

    //将整个老师类都声明成友元类
    friend class Teacher;
};

//定义一个全局函数
void fun(Stu &s)
{
    cout<<"name = "<<s.name<<"  age = "<<s.age<<"  score = "<<s.score<<endl;
}

//将老师类中的成员函数类外定义
void Teacher::display(Stu &s)
{
    cout<<"my_name = "<<name<<endl;
    cout<<"my_subject = "<<subject<<endl;

    cout<<"stu_name = "<<s.name<<endl;
}

int main()
{
    Stu s("zhangpp", 18, 90);
    fun(s);

    Teacher t("zhansan", "C++");
    t.display(s);

    return 0;
}

1.3 友元的总结

  1. 友元是单向的,A将B设置成友元,则A允许B访问自己的成员,而B不一定允许A访问其成员
  2. 友元不具有传递性:A是B的朋友,B是C的朋友,那么A不一定是C的朋友
  3. 友元不具有继承性:父类的朋友不一定是子类的朋友
  4. 友元破坏了类的封装性,使得访问权限形同虚设,所以不在万不得已的情况下,尽可能不使用友元
  5. 必须使用友元的情况:插入和提取运算符重载时,需要使用全局函数作为友元函数

二、常成员(const)

2.1 常成员函数

1> 在定义成员函数后,加关键字const,那么该函数就是常成员函数

2> 在常成员函数中,不能修改成员变量的值,保护成员变量不被修改,但是可以使用成员变量

3> 常成员函数和同名的非常成员函数,即使参数种类和个数等同,也构成重载关系,原因是this指针的类型不同

                非常成员函数中this: 类名 * const this;

                常成员函数中this:const 类名 * const this;

4> 非常对象,调用成员函数时,优先调用非常成员函数,如果没有非常成员函数,那么就调用同名的常成员函数

2.2 常对象

1> 定义对象时,加关键字const: const 类名 对象名;

2> 常对象只能调用常成员函数,不能调用非常成员函数

2.3 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) {}

    //类中的成员函数,可以直接使用所有权限下的成员
    void show()const            //this原型:const 类名  * const this;
    {
        //this->score = 100;              //更改成员变量的值
        this->age = 16;            //由于该成员变量由mutable修饰,取消了其常属性
        cout<<"name = "<<name<<"  age = "<<age<<"  score = "<<this->score<<endl;
    }

    //定义同名的非常成员函数
    void show()                   //this原型:类名  * const this;
    {
        this->score = 100;              //更改成员变量的值
        cout<<"name = "<<name<<"  age = "<<age<<"  score = "<<score<<endl;
    }
};

int main()
{
    Stu s1("zhangs", 18, 90);
    //s1.show();                      //100

    const Stu s2("lisi", 20, 50);        //该对象为常对象
    //s2.show();

    //定义常引用,目标为非常对象
    const Stu &ref = s1;

    ref.show();            // 常引用也有常属性,调用成员函数时,只能调用常成员函数,不能调用非常成员函数
    s1.show();            //调用非常成员函数

    return 0;
}

三、运算符重载(operator)

3.1 定义

所谓运算符重载,本质上也是静态多态的一种,能够实现“一符多用”,使用运算符重载,能够完成运算符作用于类对象之间,使得代码更加简洁、可读性更强。

3.2 重载的方法

所有运算符重载,都拥有一个统一的名称:operator# (#表示运算符号)

参数:根据运算符本身特点决定,如果是单目运算符,最多拥有一个参数,如果是双目运算符,最多拥有两个参数

返回值:由用户自己决定

3.3 调用原则及调用时机

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

2> 调用原则:左调右参 (运算符的左侧是函数调用者,右侧是该函数的参数) 例如:a = b; //a.operator=(b)

3.4 重载版本

每个运算符重载,都可以实现两个版本的重载函数,分别是成员函数版和全局函数版

成员函数版比全局函数版本少一个参数,因为类对象本身就是一个参数

全局函数版和成员函数版只能实现一个,否则会造成调用时的混乱情况

3.5 算术类运算符重载

1> 种类:+、-、*、/、%

2> 表达式格式:L # R //L表示左操作数 #表示运算符 R表示右操作数

3> 左操作数:既可以是左值也可以是右值,运算过程中不会被修改

4> 右操作数:既可以是左值也可以是右值,运算过程中不会被修改

5> 结果:结果是一个同类的右值,不能被改变

6> 定义格式:

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

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

3.6 关系类运算符重载

1> 种类:>、=、

2> 表达式格式:L # R //L表示左操作数 #表示运算符 R表示右操作数

3> 左操作数:既可以是左值也可以是右值,运算过程中不会被修改

4> 右操作数:既可以是左值也可以是右值,运算过程中不会被修改

5> 结果:bool类型,表示真假

6> 定义格式:

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

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

3.7 赋值类运算符重载

1> 种类:=、+=、-=、*=、/=、%=

2> 表达式格式:L # R //L表示左操作数 #表示运算符 R表示右操作数

3> 左操作数:只能是左值

4> 右操作数:既可以是左值也可以是右值,运算过程中不会被修改

5> 结果:自身的引用

6> 定义格式:

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

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

3.8 单目运算符重载

1> 种类:!、~、*、&、-

2> 表达式格式: # O                 // #表示运算符 O表示操作数

3> 操作数:既可以是左值也可以是右值,运算过程中不能更改

4> 结果:同类的右值

5> 定义格式:

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

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

#include <iostream>


using namespace std;

//定义一个复数类 5 + 3i
class Complex
{
private:
    int real;         //实部
    int vir;          //虚部
public:
    Complex() {}
    Complex(int r, int v):real(r), vir(v) {}         //有参构造
    //定义展示函数
    void show()
    {
        if(vir>=0)
        {
            cout<<real<<" + "<<vir<<"i"<<endl;
        }else
        {
            cout<<real<<vir<<"i"<<endl;
        }
    }

    //全局函数版实现加运算符重载
    friend const Complex operator+ (const Complex &L, const Complex &R);

    //成员函数版实现运算符重载
    const  Complex  operator- ( const Complex  &R)const
    {
        Complex c;
        c.real = this->real - R.real;
        c.vir = this->vir - R.vir;
        return c;
    }

    //成员函数版实现关系运算符的重载:实部>实部  && 虚部>虚部
    bool operator>(const Complex &R)const
    {
        return this->real>R.real&&this->vir>R.vir;
    }

    //重载中括号运算符
    int & operator[](int index)
    {
        if(index == 0)
        {
            return real;          //返回实部
        }else if(index == 1)
        {
            return vir;           //返回虚部
        }
    }

    //重载+=运算符:实部+=实部   虚部+=虚部
    Complex & operator+=(const Complex &R)
    {
        this->real += R.real;
        this->vir += R.vir;
        return *this;                //返回自身的引用
    }

    //重载负号运算符: 实部= -实部, 虚部 = -虚部
    Complex operator-()
    {
        Complex c;
        c.real = -this->real;
        c.vir = -this->vir;
        return c;
    }
};

//全局函数版实现加号运算符重载:实部+实部  虚部+虚部
const Complex operator+ (const Complex &L, const Complex &R)
{
    //定义一个临时空间
    Complex c;


    c.real = L.real + R.real;
    c.vir = L.vir + R.vir;


    return c;
}

int main()
{
    Complex c1(5,3);
    c1.show();                    //5+3i

    Complex c2(2,-1);
    c2.show();                      //2-1i

    Complex c3 = c1-c2;             //调用加法运算符重载函数  c1.operator-(c2)

    c3.show();                      //3+4i

    if(c3 > c2)              //调用关系运算符重载函数
    {
        cout<<"yes"<<endl;
    }else
    {
        cout<<"no"<<endl;
    }

    c3[0] = 5;               //将实部进行修改成5,调用中括号运算符重载
    c3.show();                  //5+4i

    c3 += c2;            //调用+=运算符重载函数
    c3.show();             //7+3i

    Complex c4 = -c3;      //调用-号运算符重载
    c4.show();              //-7 - 3i
    c3.show();            //7+3i

    return 0;
}

更多推荐

openGauss学习笔记-72 openGauss 数据库管理-创建和管理分区表

文章目录openGauss学习笔记-72openGauss数据库管理-创建和管理分区表72.1背景信息72.2操作步骤72.2.1使用默认表空间72.2.1.1创建分区表(假设用户已创建tpcdsschema)72.2.1.2插入数据72.2.1.3修改分区表行迁移属性72.2.1.4删除分区72.2.1.5增加分区7

GSMA SGP.21协议学习

GSMASGP.21协议学习1简介1.1概述本文档提供了一种体系结构方法,作为所有市场中设备的远程SIM配置的建议解决方案。体系结构的主要目标是为设备的远程SIM配置提供必要的凭据以获取移动网络访问权限。该版本专注于消费类市场的设备。请注意,SGP.21V1.0[23]尚未弃用。1.2范围本文档的目的是定义一个通用架构

图像识别在自动驾驶和智能安防中的关键应用

图像识别在自动驾驶和智能安防中的关键应用随着人工智能和深度学习技术的发展,图像识别已经成为了自动驾驶和智能安防领域的关键应用之一。图像识别技术能够通过处理和分析图像数据,帮助自动驾驶车辆和智能安防系统实现更准确、更高效的运行。本文将介绍图像识别在自动驾驶和智能安防中的关键应用及其相关技术。一、图像识别在自动驾驶中的应用

设计模式实战:模版方法

1.模版方法概述在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银

并发编程系列-分而治之思想Forkjoin

我们介绍过一些有关并发编程的工具和概念,包括线程池、Future、CompletableFuture和CompletionService。如果仔细观察,你会发现这些工具实际上是帮助我们从任务的角度来解决并发问题的,而不是让我们陷入线程之间如何协作的繁琐细节(比如等待和通知等)。对于简单的并行任务,你可以使用“线程池+F

数据库顶会 VLDB 2023 论文解读 - Krypton: 字节跳动实时服务分析 SQL 引擎设计

“Krypton源于DC宇宙中的氪星,它是超人的故乡,以氪元素命名”。引言近些年,在复杂的分析需求之外,字节内部的业务对于实时数据的在线服务能力也提出了更高的要求。大部分业务不得不采用多套系统来应对不同的Workload,虽然能满足需求,但也带来了不同系统数据一致性的问题,多个系统之间的ETL也浪费了大量的资源,同时对

区块链(1):区块链简介

区快链是通过密码技术保护的分布式数据库这是比特币背后的技术。本文将逐步带您了解区块链。1区块链BLOCKCHAIN的类的定义区块链有一个区块列表。它从一个单独的块开始,称为genesisblock【创世区块】2区块链BLOCK的类的定义第一个区块叫做Genesis[创世]block,每个块存储以下信息:IndexTim

Android 查看按键信息的常用命令详解

Android查看按键信息的常用命令详解文章目录Android查看按键信息的常用命令详解一、主要命令:二、命令详解1、getevent2、getevent-l3、dumsysinput4、catXXX.kl4、cat/dev/input/eventX5、getevent其他命令6、inputkeyeventXX三、简单

【性能测试】JMeter:集合点,同步定时器的应用实例!

一、集合点的定义在性能测试过程中,为了真实模拟多个用户同时进行操作以度量服务器的处理能力,可以考虑同步虚拟用户以便恰好在同一时刻执行操作或发送请求。通过插入集合点可以较真实模拟多个用户并发操作。(注意:虽然通过加入集合点可以约束请求同时发送,但不能确保请求同时到达服务器,所以只能说是较真实模拟并发)在JMeter中可以

Android SurfaceFlinger导读(03)MessageBase

该系列文章总纲链接:AndroidGUI系统之SurfaceFlinger系列文章目录说明:关于导读:导读部分主要是方便初学者理解SurfaceFlinger代码中的机制,为后面分析代码打下一个更好的基础,这样就可以把更多的精力放在surfaceFlinger的业务逻辑分析上。关于代码分支:以下代码分析均在androi

用Jmeter进行压测详解

简介:1.概述一款工具,功能往往是很多的,细枝末节的地方也很多,实际的测试工作中,绝大多数场景会用到的也就是一些核心功能,根本不需要我们事无巨细的去掌握工具的所有功能。所以本文将用带价最小的方式讲解如何快速上手使用jmeter来进行压测。JMeter,一款接口测试工具,是Java程序,需要JDK环境,建议使用JDK8或

热文推荐