目录
- 1.面向过程和面向对象初步认识
- 2.类的引入
- 3.类的定义
- 4.类的访问限定符及封装
- 4.1访问限定符
- 4.2封装
- 5.类的作用域
- 6.类的实例化
- 7.类对象模型
- 7.1 如何计算类对象的大小
- 8.this关键字
如果说我们对C++的初步认识,是觉得C++是对C语言不足之处的进行修补,在认识完类与对象后,我们会对C++产生新的认识
1.面向过程和面向对象初步认识
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题;
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成;
2.类的引入
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。 比如: 之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现, 会发现struct中也可以定义函数
对于C++中的struct:属性(成员变量)+方法(函数)
①兼容C中struct中的所有用法
②升级成了类
举例如下:
typedef int DataType;
//Stack为类名
struct Stack
{
//①属性
DataType *_array;
size_t _capacity;
size_t _size;
//②方法
void Init(size_t capacity)
{
//...
}
void Push(const DataType &data)
{
//...
}
void Destroy()
{
//...
}
};
int main()
{
//s是对象
Stack s;
//【对象.方法】、【对象.属性】
s.Init(10);
s.Push(1);
s.Push(2);
s.Push(3);
cout << s.Top() << endl;
s.Destroy();
return 0;
}
一般的,在定义成员变量名时,常常在前面加上前杠,例如:
class Date
{
public:
int _year;
int _month;
int _year;
public:
void Init(){
//...
}
}
3.类的定义
1.定义
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
class为定义类的关键字, ClassName为类的名字, **{}**中为类的主体,注意类定义结束时后面分号不能省略
类的两种定义方式:
①声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理
class People {
public:
int age;
int sex;
int height;
public:
void Eat() {
cout << "people can eat" << endl;
}
void Run() {
cout << "people can run" << endl;
}
};
②类声明放在.h文件中,成员函数定义放在.cpp文件中,注意: 成员函数名前需要加类名
为什么要声明和定义分离?
加强代码的可阅读性!
在cpp文件种对成员函数进行声明,注意标识,该函数的类域(即类名)
2.对象
和结构体一样,类的定义是一个类型,要访问它的成员(属性、方法)就要实例化对象
一个类可以定义多个对象
4.类的访问限定符及封装
4.1访问限定符
① public修饰的成员在类外可以直接被访问
② protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
③访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
④如果后面没有访问限定符,作用域就到 } 即类结束
⑤class的默认访问权限为private ,struct默认为public(因为struct要兼容C)
4.2封装
面向对象的三大特性: 封装、继承、多态
在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
因此我们将属性置为private,通过public方法(公有成员)来实现对属性的操作
因此,封装的目的不是为了阻止对数据的访问,而是规定一种规范的方法,让我们对其访问
5.类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中 ;在类体外定义成员时,需要使用**:😗*作用域操作符指明成员属于哪个类域
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
cout << _name << " " << _gender << " " << _age << endl;
}
6.类的实例化
常见错误:
Person::_age=1; //wrong
Person::PrintPersonInfo(); //wrong
正确的:
//通过对象
Person p1;
p1._age=10;
p1.PrintPersonInfo();
//通过指针
Person p2;
Person* ptr=&p2;
ptr->_age=20;
7.类对象模型
7.1 如何计算类对象的大小
引入:
class A
{
public:
void PrintA()
{
cout << _a << endl;
}
private:
char _a;
};
这里类A的大小是多少呢?
结果是1,我们发现类存储时只存了变量(属性)
而类对象的大小规则,同结构体内存对齐规则
【补充】:结构体内存对齐规则
1.第一个成员在与结构体偏移量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
注意:对齐数 = 编译器默认的一个对齐数与该成员大小的较小值,VS中默认的对齐数为8
3.结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
①例:内存对齐的体现
②例:空类大小为1——占位,不存储有效数据,标识对象存在
8.this关键字
1.引入:
class Date {
private:
int _year;
int _month;
int _day;
public:
void Init(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
public:
void Print() {
cout << _year << " " << _month << " " << _day << endl;
}
};
对于上述类,有这样的一个问题:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即: C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象)
即:
void Print(Date* this)
{
cout << this->_year << " " << this->_month << " " << this->_day << endl;
}
注意:this指针的定义和传递是由编译器完成,我们不能去做,但是我们可以在类里面用this指针
这里的this代表d1和d2对象的地址:
this指针与const修饰问题:
const Date* p1; //修饰的是指针指向的对象 *p1
Date const* p2; //修饰的是指针指向的对象 *p2
Date* const p3; //修饰的是本身 p3
2.this指针存在哪?
this是一个形参,一般存在栈帧中(vs下进行了优化,使用了ecx寄存器传递)
3.this指针可以为空吗?
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A *p = nullptr; //p这里是一个空指针
p->Print(); //不发生解引用,因为成员函数的地址不在对象中,在公共代码区域
return 0;
}
//正常运行
// 2.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void PrintA()
{
cout << _a << endl; //本质是this->_a
}
private:
int _a;
};
int main()
{
A *p = nullptr; //发生空指针的解引用
p->PrintA();
return 0;
}
//运行崩溃