文章目录
- 1.面向过程与面向对象
- 2.类的引入
- 3.类的定义
- 两种定义方式
- 4.类的访问限定符
- 5.类的作用域
- 6.类的示例化
- 7.类的对象大小计算
- 8.类成员函数的this指针
- C语言和C++的对比(this指针)
- 空指针的问题
- C语言和C++实现Stack对比
1.面向过程与面向对象
- C:面向过程,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
- C++:面向对象,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
2.类的引入
- C:struct 结构体,定义成员变量
- C++:struct 定义成员变量和函数
C++ 把 结构体“升级成了” 类 ,struct
→ class
//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
{
类体:由“类的成员”组成
……
};
- 类的成员:
- 变量 → 类的属性/成员变量
- 函数 → 类的方法/成员函数
两种定义方式
- 声明和定义全在类体中 → 日常练习(注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。)
内敛函数的声明和定义不能分离,所以声明的时候就要直接定义。
代码量小、比较简单的函数一般直接在类中定义。
//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;
};
- 声明放在.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.类的访问限定符
- 公有(public)→
struct
默认 - 保护(protected)
- 私有(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,3
int _month;//4→ 4,5,6,7
int _day;//4→ 8,9,10,11
//内存:12byte
};
int main()
{
DateRB d1;//类的实例化
d1.Init();
cout << sizeof(d1) << endl;//output:12
return 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指针)
对象 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;
}
ptr->func();
//正常运行
- 为什么会正常运行?
- 此过程中,调用 func 函数,只是把 ptr 的内容(
nullptr
)传给了this 指针
,而函数运行过程中 没有对 this 指针解引用操作。
- 此过程中,调用 func 函数,只是把 ptr 的内容(
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 指针
解引用操作,对空指针解引用造成运行崩溃
(*ptr).func();
//正常运行
和ptr->func();
一样,没有对this 指针解引用
- sum.看到
'*'
或者'->'
不一定进行了解引用操作
注意:不能这样调用函数👇
func();
DateRB::func();
C语言和C++实现Stack对比
-
CPP:
- 数据和方法都封装到类里面
- 控制访问方式(共有、私有)
-
C:
- 数据和方法分离
- 数据的访问控制是自由的,不受限制的
-
譬如:取栈顶的元素,可以不通过调用函数的方式而直接访问👇
-
cpp Stack st; int top = st.a[st.top]
但是这需要了解栈的底层结构,并且容易出错
-
END