> 文章列表 > C++ -2- 类和对象(上)| 什么是类

C++ -2- 类和对象(上)| 什么是类

C++ -2- 类和对象(上)| 什么是类

文章目录

  • 1.面向过程与面向对象
  • 2.类的引入
  • 3.类的定义
    • 两种定义方式
  • 4.类的访问限定符
  • 5.类的作用域
  • 6.类的示例化
  • 7.类的对象大小计算
  • 8.类成员函数的this指针
    • C语言和C++的对比(this指针)
    • 空指针的问题
  • C语言和C++实现Stack对比

1.面向过程与面向对象

  • C:面向过程,关注的是过程分析出求解问题的步骤,通过函数调用逐步解决问题。
  • C++:面向对象,关注的是对象将一件事情拆分成不同的对象,靠对象之间的交互完成。

2.类的引入

  • C:struct 结构体,定义成员变量
  • C++:struct 定义成员变量和函数

C++ 把 结构体“升级成了” structclass

//C:
struct ListNode
{int* _a;int _size;int _capacity;
};//CPP:
struct ListNode2
{void Init(size_t capacity){_a = (int*)malloc(sizeof(int) * capacity);if (!_a){perror("malloc fail");return;}_capacity = capacity;_size = 0;}//……int* _a;int _size;int _capacity;
};

3.类的定义

class classname
{    类体:由“类的成员”组成……
}
  • 类的成员:
    • 变量 → 类的属性/成员变量
    • 函数 → 类的方法/成员函数

两种定义方式

  1. 声明和定义全在类体中 → 日常练习(注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。)

内敛函数的声明和定义不能分离,所以声明的时候就要直接定义。
代码量小、比较简单的函数一般直接在类中定义。

//CPP:
struct ListNode2
{void Init(size_t capacity){_a = (int*)malloc(sizeof(int) * capacity);if (!_a){perror("malloc fail");return;}_capacity = capacity;_size = 0;}//……int* _a;int _size;int _capacity;
};
  1. 声明放在.h头文件,定义放在.cpp文件 → 正式工作
//.h
class fantasy
{void Init(int capacity = 4);//……int* _a;int _size;int _capacity;
};//.cpp
#include "test.h"
void fantasy::Init(int capacity)//意思是“Init”这个函数是“fantasy”这个类里的成员函数
{_a = (int*)malloc(sizeof(int) * capacity);if (!_a){perror("malloc fail");return;}_capacity = capacity;_size = 0;
}
  • 成员变量命名规则的建议:
class DateRB
{
public:void Init(int year = 0, int month = 0, int day = 0){year = year;month = month;day = day;//这样就很奇怪,到底是谁赋值给谁}
private:int year;int month;int day;
};

所以,我们建议区别命名:

class DateRB
{
public:void Init(int year = 0, int month = 0, int day = 0){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};

4.类的访问限定符

  1. 公有(public)→ struct 默认
  2. 保护(protected)
  3. 私有(private)→ class 默认

一般:

class MyClass
{
public:成员函数;private:成员变量;
};
  • 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

5.类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。

class fantasy//新的作用域
{void Init(int capacity = 4);//……int* _a;int _size;int _capacity;
};void fantasy::Init(int capacity)//指明成员属于哪个类域。
{_a = (int*)malloc(sizeof(int) * capacity);if (!_a){perror("malloc fail");return;}_capacity = capacity;_size = 0;
}

6.类的示例化

类 → 相当于设计图
类的实例化 → 相当于根据设计图建房

class DateRB
{
public:void Init(int year = 0, int month = 0, int day = 0){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{DateRB d1;//类的实例化 → 对象d1.Init();return 0;
}

7.类的对象大小计算

class DateRB
{
public:void Init(int year = 0, int month = 0, int day = 0){_year = year;_month = month;_day = day;}
private:int _year;//4→ 0,1,2,3int _month;//4→ 4,5,6,7int _day;//4→ 8,9,10,11//内存:12byte
};int main()
{DateRB d1;//类的实例化d1.Init();cout << sizeof(d1) << endl;//output:12return 0;
}

如上这个例子,sizeof(d1) = 12(byte)

  • 为什么成员变量在对象(d1)里,成员函数不在对象里?
    • 每个对象的成员变量不一样,需要单独存储
    • 调用的成员函数都是一样的,在公共区域 (代码段)

特别的:

// 类中仅有成员函数
class A2 {
public:void f2() {}
};
// 类中什么都没有---空类
class A3
{};sizeof(A2) : ?
sizeof(A3) : ?cout << sizeof(A2) << endl;//output:1
cout << sizeof(A3) << endl;//output:1

1 字节 不存储有效数据,只是占位,用来标识 对象被实例化出来。


8.类成员函数的this指针

在这里插入图片描述

C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。这个过程编译器自动完成。

void Init(int year = 0, int month = 0, int day = 0)
{_year = year;_month = month;_day = day;
}//实际上:
void Init(DateRB* this, int year = 0, int month = 0, int day = 0)
{this->_year = year;this->_month = month;this->_day = day;
}

C语言和C++的对比(this指针)

在这里插入图片描述
C++ -2- 类和对象(上)| 什么是类
对象 d1 调用函数时,this = &d1 这个地址会传给函数

  • this 指针 → 隐含的形参,储存在

空指针的问题

定义:DateRB* ptr = nullptr;

class DateRB
{
public:void Init(int year = 0, int month = 0, int day = 0){_year = year;_month = month;_day = day;}void func(){}
private:int _year;int _month;int _day;
};int main()
{DateRB* ptr = nullptr;ptr->func();//正常运行ptr->Init(2023, 2, 27);//运行崩溃(*ptr).func();//正常运行return 0;
}
  1. ptr->func();//正常运行
    C++ -2- 类和对象(上)| 什么是类
  • 为什么会正常运行?
    • 此过程中,调用 func 函数,只是把 ptr 的内容(nullptr)传给了 this 指针,而函数运行过程中 没有对 this 指针解引用操作
  1. ptr->Init(2023, 2, 27);//运行崩溃
void Init(int year = 0, int month = 0, int day = 0)
{this->_year = year;this->_month = month;this->_day = day;
}

显然,Init 函数会对 this 指针 解引用操作对空指针解引用造成运行崩溃

  1. (*ptr).func();//正常运行
    ptr->func();一样,没有对this 指针解引用
  • sum.看到 '*' 或者 '->' 不一定进行了解引用操作

注意:不能这样调用函数👇

func();
DateRB::func();

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

  • CPP:

    1. 数据和方法都封装到类里面
    2. 控制访问方式(共有、私有)
  • C:

    1. 数据和方法分离
    2. 数据的访问控制是自由的,不受限制的
      • 譬如:取栈顶的元素,可以不通过调用函数的方式而直接访问👇

      • cpp Stack st; int top = st.a[st.top] 但是这需要了解栈的底层结构,并且容易出错


END