【c++笔记】类和对象流食般投喂(上)

声明:以下知识相关资料来自比特官网和小编手搓~
类和对象(上)

++1、类的定义++

1.1 类定义格式

1.2 访问限定符

1.3 类域

++2、实例化++

2.1 实例化概念

2.2 对象大小

++3、this指针++

++4.C++和C语言实现Stack对比++

1、类的定义:

在C++中,本贾尼祖师爷对C语言进行了创新再改造,对于自定义类型的创建设计出C++新有的 类 来进行相关操作。

1.1、类定义的格式:

class是定义类的关键字,后跟你要创建自定义类型的具体名称,这个具体名在往后就可以直接当类型使用,接着 {} 内是类具体要定义的主体,里面可以定义 变量 以及 函数 ,最后不要忘记 {} 后面要加 ; 。

在类主体里面定义的变量叫做成员变量(或是成员属性);在类主体定义的函数叫做成员函数(或是成员方法)。

为了区分变量和让形参有更好的可读性,一般会习惯让变量加上特殊标识符,比如现有变量 a ,加上特殊标识符有 a/a/m_a,这些要求就看公司具体习惯了,我们后面的代码样例选用第一个作为变量的添加特殊标识符的例子。

cpp 复制代码
class Date
{
public:
    void Init(int year, int month, int day)
    {
    _year = year;
    _month = month;
    _day = day;
    }

private:
    // 为了区分成员变量,⼀般习惯上成员变量
    // 会加⼀个特殊标识,如_ 或者 m开头
    int _year; // year_ m_year
    int _month;
    int _day;
};

​

C++中也是可以用struct定义类的,在此C++对C语言中的struct进行了升级,定义的方法和class关键字一样,最大的区别就是C++的struct升级后主体内可以创建函数。struct后面跟的具体名,可以直接当定义的类的类型用了,不用像C语言那样麻烦的再加struct才能在主体中用。

cpp 复制代码
// C++升级struct升级成了类
// 1、类⾥⾯可以定义函数
// 2、struct名称就可以代表类型

// C++兼容C中struct的⽤法
typedef struct ListNodeC
{
    struct ListNodeC* next;
    int val;
}LTNode;

// 不再需要typedef,ListNodeCPP就可以代表类型
struct ListNodeCPP
{
    void Init(int x)
    {
    next = nullptr;
    val = x;
    }

    ListNodeCPP* next;
    int val;
};

定义在类里面的成员函数,编译器默认是内联函数(inline)。

1.2&1.3、访问限定符&类域

类的形成同时伴随着类域的形成,就像是在已有空间上又开辟了一个新的空间,空间与空间之间是有空间壁垒相阻隔的,空间之间想要完成交互,就需要将空间壁垒打破,或是有特有的时空通道连接两空间。所以类域这个空间,其实本身就是封闭的,不与外界空间有通道的。

在类中,有3个访问限定符,分别是public、private和protected,public就像是一条授权的时空通道,但是这条时空通道是被访问空间的内部规则所影响的,public是这条时空通道的起始宽度点,从这个起始开始点开始,第一次遇到他的终止宽度点(private,protected和 };),这始末宽度差就是这条时空通道的真实宽度,而被private和protected所修饰的部分,就像是被访问空间的禁地般,当然,这个禁地的领域范围计算与空间通道计算方式一样,对于禁地来说,设置着作用域访问操作符 (::)这样的令牌,如果有这禁地的访问令牌也是一样可以拜访禁地的。

类域影响的是编译的查找规则。默认情况,编译器编译时,只会在全局域和局部域去查找,类域如果没有建时空通道,类域禁区没有给编译器访问令牌,编译器编译时,是不会去访问的,如此,编译器就像是全局域和局部域的空间之主,类域不是他直接管辖的空间。

cpp 复制代码
class Stack
{
public:
    // 成员函数
    void Init(int n = 4);//声明

private:
    // 成员变量
    int* array;
    size_t capacity;
    size_t top;
};
    
    // 声明和定义分离,需要指定类域
    void Stack::Init(int n)//定义
    {
        array = (int*)malloc(sizeof(int) * n);
        if (nullptr == array)
        {
            perror("malloc申请空间失败");
            return;
        }
        capacity = n;
        top = 0;
    }

2、实例化

2.1、实例化概念

用自定义类类型创建的对象就是实例化的具体体现,自定义类就像是一张房子的工程图纸,所有的一切都是纸面上的,不具有实用性,只有通过这个图纸建造出来的房子,也就是对象,他才有实用性,才可以住"人"。当然,通过一张图纸可以建造多个一样户型的房子,同一个自定义类类型可以实例化出多个对象。

自定义类里面的成员对象都只是声明,并没有开空间,只有类实例化出对象时,才会分配空间。

cpp 复制代码
int main()
{
    // Date类实例化出对象d1和d2
    Date d1;
    Date d2;

    return 0;
}

2.2、对象大小

在了解对象大小之前,我们应先弄清楚,用自定义类类型定义的对象中具体包含了什么?类实例化出的每个对象都有独立的数据空间,所以对象中肯定包含成员变量,但是不包含成员函数,函数在编译之后会变成一条汇编指令 [call地址] 存储在代码段中,对象中也不用存储函数指针(函数的地址),因为在编译链接时,编译器就需要找到函数的地址了,而像动态多态,在代码运行时找函数地址,所以需要存储函数地址。

类实例化出的对象同样也要遵守内存对齐的规则,其规则与C语言结构体遵守的内存对齐规则一致,如下:

cpp 复制代码
#include<iostream>
using namespace std;
    // 计算⼀下A/B/C实例化的对象是多⼤?
class A
{
public:
    void Print()
    {
    cout << _ch << endl;
    }
private:
    char _ch;
    int _i;
};

class B
{
public:
    void Print()
    {
    //...
    }
};

class C
{};

int main()
{
    A a;
    B b;
    C c;
    cout << sizeof(a) << endl;
    cout << sizeof(b) << endl;
    cout << sizeof(c) << endl;
    return 0;
}

看完上述代码有没有疑惑,为什么B类和C类并没有成员变量,运行结果会弹1呢?

原因:只是标记一下这个类存在过,弹0,会被误认为B类和C类并没有存在过。

3、this指针

上面提到过,用类类型实例化出的对象并不包含成员函数,成员函数是放在公共代码区的,等待着对象调用,为了方便区别是哪个对象的,就会有一个叫做this的翻译官,有调用时,就把调用对象的身份(对象的地址)给函数。这个翻译官是秘密的,只有在类里面才能显示调用,形参实参不能显示调用this指针,可以理解为函数不想让外人觉得他是废物,还要翻译官帮忙。

this指针默认是形参的第一个参数,是一个当前类类型的指针,类中的成员函数访问成员变量本质是通过this指针访问的,对于this指针,编译器在编译时会自己处理。

cpp 复制代码
#include<iostream>
using namespace std;

class Date
{
public:
    // void Init(Date* const this, int year, int month, int day)
    //Date* const this,这个是限定了指针指向的地址不可修改,指向的内容可修改

    void Init(int year, int month, int day)
    {
        // 编译报错:error C2106: "=": 左操作数必须为左值
        // this = nullptr;
        // this->_year = year;
        _year = year;
        this->_month = month;
        this->_day = day;
    }
    void Print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

private:
    // 这⾥只是声明,没有开空间
    int _year;
    int _month;
    int _day;
};

int main()
{
    // Date类实例化出对象d1和d2
    Date d1;
    Date d2;
    // d1.Init(&d1, 2024, 3, 31);
    d1.Init(2024, 3, 31);
    d1.Print();
    d2.Init(2024, 7, 5);
    d2.Print();
    return 0;
}

来点题目试试水:

cpp 复制代码
#include<iostream>
using namespace std;
class A
{
public:
    void Print()
    {
    cout << "A::Print()" << endl;
    }
private:
    int _a;
};

int main()
{
    A* p = nullptr;
    p->Print();   //p是一个类类型对象的指针,Print()是类中的成员函数,并不在对象里面,
                  //所以就不存在对空指针的解引用,算是调用
    return 0;
}
cpp 复制代码
#include<iostream>
using namespace std;
class A
{
public:
    void Print()
    {
        cout << "A::Print()" << endl;
        cout << _a << endl;   //实则出现了对空指针的解引用
                              // cout << this->_a << endl;
    }
private:
    int _a;
};

int main()
{
    A* p = nullptr;
    p->Print();
    return 0;
}


this指针本质是形参,是局部变量。

4、C++和C语言实现Stack对比

类的学习可以让我们感受一下封装~
C++中数据和函数都放到了类⾥⾯,通过访问限定符进⾏了限制,不能再随意通过对象直接修改数 据,这是C++封装的⼀种体现,这个是最重要的变化。这⾥的封装的本质是⼀种更严格规范的管 理,避免出现乱访问修改的问题。
C++中有⼀些相对⽅便的语法,⽐如Init给的缺省参数会⽅便很多,成员函数每次不需要传对象地 址,因为this指针隐含的传递了,⽅便了很多,使⽤类型不再需要typedef⽤类名就很⽅便。

相关推荐
xyq20241 小时前
Lua 模块与包
开发语言
是个西兰花1 小时前
单列模式和C++中的类型转换
c++·单例模式·设计模式·rtti
RainCity1 小时前
Java Swing 自定义组件库分享(四)
java·笔记·后端
小短腿的代码世界1 小时前
打印不止是QPrinter:深入Qt Print Support框架的内核设计与跨平台输出管道
开发语言·qt
性野喜悲1 小时前
python将excel中的链接转成图片并替换链接展示在excel中【将pdf的第一页插入excel并将对应信息获取到插入签名等位置】
开发语言·python·excel
诙_1 小时前
C++代码实践应用
开发语言·c++
谙弆悕博士1 小时前
【附C语言源码】从零实现命令行贪吃蛇游戏
c语言·开发语言·学习·游戏·游戏程序·小游戏·贪吃蛇
Evand J1 小时前
【无人机编队控制程序4】复杂障碍环境下多无人机编队避障(人工势场法APF)与协同控制,MATLAB仿真例程
开发语言·matlab·无人机·控制·apf·避障
Little At Air1 小时前
LinuxOS阻塞队列模型(单生产者单消费者)
linux·数据结构·c++