2021 1.23 逆向分享会稿 Ver 1.0

主题:C++ 细碎知识杂讲

主讲:Menou

参考书籍:《C++从入门到入土》

内容:

1、结构体(struct)和共用体(union)

我们学习过结构体,结构体可以理解成一个广义的,加强版的数组,结构体较于数组的优势是可以将多样的信息存放在一个相邻的,有意义的内存(域?)内。而数组只能存放单一类型的信息。

而共用体是使用覆盖技术,允许不同类型的变量存放到同一段内存单元内,几个变量互相覆盖的一种数据类型。

union 共用体名

{

    成员列表

};

我们通过这种方式定义共用体。共用体名可以省略。

定义共用体的方式和结构体一样有三种,他们都是等效的。

如何输出共用体成员变量?我们写个简单的程序,顺便验证一下共用体的特性。

#include<iostream>
#include <cstring>

using namespace std;

int main(){

    union Mydata{
        int i;
        float f;
        char str[50];
    }datel;

    datel.i=56;
    datel.f=150.33;
    strcpy(datel.str,"Beautiful World");
    cout<<"datel.i=="<<datel.i<<endl;
    cout<<"datel.f=="<<datel.f<<endl;
    cout<<"datel.str=="<<datel.str<<endl;

    return 0;
}

输出结果:

/Users/Menou/untitled12/cmake-build-debug-/untitled12
datel.i==1969317186
datel.f==2.85723e+32
datel.str==Beautiful World

进程已结束,退出代码为 0

对照上述代码,发现 i 和 f 值都是垃圾数据,证明 i 和 f 被“覆写”了。

既然如此,我们不妨改进一下程序:

#include<iostream>
#include <cstring>

using namespace std;

int main(){

    union Mydata{
        int i;
        float f;
        char str[50];
    }datel;

    datel.i=56;
    cout<<"datel.i=="<<datel.i<<endl;
    datel.f=150.33;
    cout<<"datel.f=="<<datel.f<<endl;
    strcpy(datel.str,"Beautiful World");
    cout<<"datel.str=="<<datel.str<<endl;

    return 0;
}

改进后,结果果真是正常的

/Users/Menou/untitled12/cmake-build-debug-/untitled12
datel.i==56
datel.f==150.33
datel.str==Beautiful World

进程已结束,退出代码为 0

顺便补充一个知识,关于用户自定义的数据类型typedef,在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。说完了介绍,下边说用法:

1、最简单的,给已知变量起个新名字

typedef long byte_4;

这个很容易理解

2、稍微复杂,与结构体结合

typedef struct tagMyStruct
{
 
 
    int iNum;
 
    long lLength;
 
 
}MyStruct;

这一步实际上完成两个操作:其一,定义一个新的结构类型 struct tagMyStruct;其二,typedef为这个新的结构起了一个名字,叫MyStruct。

MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。

在看CSDN文的时候发现一个没有注意到的有趣问题,就是

typedef struct tagNode
{
 
    char* pItem;
 
    pNode* pNext;
 
}pNode;

但是IDE里边报错了,为什么?

报错的是第二个指针,错误原因是:Unknown type name 'pNode';不知道‘pNode’?但我在结构体最后不是定义了吗??我们接着往下看。 

学过数据结构的同学知道,C++语言是允许在结构中包含指向它自己(结构)的指针,我们在链表的结点等见过很多这样的操作。因此第二个指针看起来是合理的。

根据我们上面的阐述可以知道:新结构建立的过程中遇到了 pNext 域的声明,类型是 pNode ,但是,我们要知道,pNode 表示的是类型的新名字,在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候IDE根本不认识什么叫 pNode 。

如何解决?我们可以从链表的建立中得到思路,既然链表是分为链表表结构以及表中元素(结点)结构两部分来建立的(依据我们学校开设的课程,我们是用类来处理两个结构,即建立表类,以及结点类两个类):

struct tagNode
{
 
    char* pItem;//不变
 
    struct tagNode* pNext;//指向tagNode的指针,
                            //这里的tagNode是被编译器所理解的,因此合法
};
 
typedef struct tagNode* pNode;//再利用typedef的功能,将这个指针“命名”为pNode

再者,typedef可以和#define命令互换?这个就讲到这吧,到这就差不多了。

2、类(Class)的知识

首先明确,类是一个数据结构,他包含对象的“属性”(是什么),和“操作”(能干啥)。对象是类的实例(即具体的类)。

所谓面向对象,其实就是九个概念,类、类变量、数据成员、方法重写、实例变量、继承、实例化、方法、对象。

对于类的定义和类的访问修饰符(public、private、protected保护类)、类的构造/析构函数等请读者自查自学。

下边讲点零碎的,读者可能没有注意到的知识。

1、指向对象的指针

为什么是指向对象的指针而不是指向类的指针?因为类只是种结构,而对象才是实实在在的数据,指针指而有物。

其二,在指向对象的指针成功赋值后,例如:

#include<iostream>
#include <cstring>

using namespace std;

class Students{
public:
    char name[10];//name
    int No;//序号
    int score;//score

    Students(){
        cout<<"创建完成"<<endl;
    };
    ~Students(){
        cout<<"解构完成"<<endl;
    };
    void OutPut(){
        cout<<"Name:"<<name<<" No:"<<No<<" score:"<<score<<endl;
    }
}student;//简易的学生成绩记录,如上每个学生由以上成员组成

int main(){
    Students Klee;
    Students *p=&Klee;//记得要加取地址符
    cin>>p->name;
    cin>>p->No;
    cin>>p->score;

    cout<<p->name<<endl;
    cout<<p->No<<endl;
    cout<<p->score<<endl;

    return 0;
}

结果:

/Users/yuwenao/untitled12/cmake-build-debug-/untitled12
创建完成
创建完成
Klee
13
299
Klee
13
299
解构完成
解构完成

进程已结束,退出代码为 0

可见,指向对象的指针指向具体对象后可以用“->“来访问类元素。

2、友元函数和友元类

我们知道,由于类对于信息的封装,我们通过对象仅可访问public成员,只有本类的函数可以访问类的 private 类型。而有一种特殊情况就是,借助友元(friend),可以使得其他类 中的成员函数以及全局范围的函数访问当前类的 private 成员和 protected 成员。

友元不是成员函数,但是它可以访问类中的私有成员。

友元函数:

#include<iostream>
#include <cstring>

using namespace std;

class Students{
private:
    char name[10];//name
    int No;//序号
    int score;//score
public:
    Students(){
        cout<<"创建完成"<<endl;
    };
    ~Students(){
        cout<<"解构完成"<<endl;
    };

    friend void OutPut(Students *pp);//讲输出函数设置为友函数
    friend void InPut(Students *pp);//讲输入函数设置为友函数



};//简易的学生成绩记录,如上每个学生由以上成员组成
void InPut(Students *pp)
{
    //由此可以在不添加任何类头衔的情况下编写函数
    cin>>pp->name>>pp->No>>pp->score;

}

void OutPut(Students *pp)
{
    //由此可以在不添加任何类头衔的情况下编写函数
    cout<<"Name:"<<pp->name<<" No:"<<pp->No<<" score:"<<pp->score<<endl;
}


int main(){
    Students Klee;
    Students *p=&Klee;//记得要加取地址符
    cout<<"//输入//"<<endl;
    InPut(&Klee);
    cout<<"//输出//"<<endl;
    OutPut(&Klee);

    return 0;
}

基于上边的程序稍微修改:

1、将学生姓名、学号、成绩等设为私有成员;

2、将创建对象、解析对象函数设为公有成员;

3、编写全新 的输入输出函数,并在类里边将两个 IO 函数设为友元函数。

查看结果

/Users/yuwenao/untitled12/cmake-build-debug-/untitled12
创建完成
//输入//
Klee
13
278
//输出//
Name:Klee No:13 score:278
解构完成

进程已结束,退出代码为 0

 此时两个友元函数不属于任何类,他是全局的。

友元类

这种情况下,整个类都是友元。若要声明函数为一个类的友元,需要在函数原型最前边加上 friend 关键字。如下:

#include<iostream>

using namespace std;


class Address;

class Students{
private:
    char name[10];//name
    int No;//序号
    int score;//score
public:
    Students(){
        cout<<"创建完成"<<endl;
    };
    ~Students(){
        cout<<"解构完成"<<endl;
    };

    void OutPut(Address *sp);//讲输出函数设置为友函数
    friend void InPut(Students *pp);//讲输入函数设置为友函数



};//简易的学生成绩记录,如上每个学生由以上成员组成
void InPut(Students *pp)
{
    //由此可以在不添加任何类头衔的情况下编写函数
    cin>>pp->name>>pp->No>>pp->score;

}


class Address{
private:
    char country[20];
    char Working_Place[60];
public:
    void IIPut(){
        cin>>country;
        cin>>Working_Place;
    }


    //讲Students声明为友元类
    friend class Students;

};

void Students::OutPut(Address *sp)
{
    //由此可以在不添加任何类头衔的情况下编写函数
    cout<<"Name:"<<name<<" No:"<<No<<" score:"<<score<<endl;
    cout<<"County:"<<sp->country<<" Working Place:"<<sp->Working_Place<<endl;
}
int main(){
    Students Klee;
    Students *spl = &Klee;
    Address KleeL;
    Address *apl = &KleeL;
    cout<<"INPUT:"<<endl;
    InPut(spl);
    apl->IIPut();
    cout<<"OUTPUT:"<<endl;
    spl->OutPut(apl);

    return 0;
}

淦,这段码改 bug 改了一个小时,吐了

/Users/yuwenao/untitled12/cmake-build-debug-/untitled12
创建完成
INPUT:
Klee
9
297
Mondstadt
Knights_of_the_XiFeng
OUTPUT:
Name:Klee No:9 score:297
County:Mondstadt Working Place:Knights_of_the_XiFeng
解构完成

进程已结束,退出代码为 0

虽然这段程序有点不切换现实,怎么可能让辣么可爱的可莉去考试呢?但还是姑且写出来。

修改:1、将 Students 类的友函数 OutPut 删除,新增 Students 类新成员函数 OutPut。

2、新增类 Address,Students 类是 Address 类的友元类。

3、修改了 OutPut,利用友元类的特性在 Students 的成员函数中输出 Address 的成员数据。 

由此,我们理解了友元类。

最后提醒:友元关系是单向的。即:Students 是 Address 的友元,但是 Address不是 Students 的友元。Students 是 Address 的友元意味着 Students 可以使用“朋友”的成员数据。反之则不然。

最后,因为友元关系的单向性,由此友元关系也不能传递。 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值