面向过程和面向对象的初步认识
C语言是基于面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
一:类的定义
class为定义类的关键字,Person为类的名字,{}中为类的主体,注意类定义结束时后面的分号。
类中的元素称为类的成员:类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数。
成员函数的声明和定义可以全部放在类体中,也可以将声明放在 .h 文件中,定义放在.cpp文件中。
- 举个栗子
class Person{
public:
// 成员函数
void PrintPerson();
private:
// 成员变量
char _name[20];
char _gender[3];
int _age;
……
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo(){
cout<<_name<<" "_gender<<" "<<_age<<endl;
}
二:类的访问限定符及封装
- 访问限定符
public: 修饰的成员可以在类外被访问(公有的)
private: 修饰的成员在类外不能直接被访问(私有的)
class的默认访问权限为private | struct的默认访问权限为public(兼容C)
注:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
【面试题】: C++中的struct和class的区别是什么?
解答:C++是兼容C语言的,C++中的struct可以当成结构体使用。另外C++中struct还可以用来定义类,struct定义类和class定义类相似,区别是struct的成员默认访问权限为public,class的成员默认访问权限为private。
- 封装
封装从字面上来理解就是包装的意思,专业点就是信息的隐藏。
封装: 封装本质上是一种管理,用类将数据和操作数据的方法结合起来,通过访问限定符隐藏对象的属性和实现细节,仅公开接口与外部交互。
也就是说用户是无需知道对象内部的细节,但可以通过该对象对外的提供的接口来访问该对象。
封装的优点: 提高了数据的安全性(外部不能随意访问私有的成员属性)、提高了代码的可维护性(只需要修改类就可以)、降低了代码的耦合度(使代码模块化)。
三:类的实例化
类的实例化:用类创建对象的过程
- 类只是一个模型,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
- 一个类可以实例化出多个对象,对象占用实际的物理空间,存储类的成员变量。
类相当于设计图纸(设计出来什么东西,但实际不占空间)对象相当于使用图纸建造出来的房子。
- 举个栗子
class AddClass{
private:
int _num1;
int _num2;
public:
//默认内联但为了可读性我们还是加上内联的标志
inline void GetNum1(int num1){
_num1 = num1;
}
inline void GetNum2(int num2){
_num2 = num2;
}
//在类内声明
int AddNum();
};
//在类外定义
int AddClass::AddNum(){
return _num1 + _num2;
}
int main(){
AddClass add;//类的实例化
add.GetNum1(1);//成员调用
add.GetNum2(2);
cout << add.AddNum() << endl;
}
四:如何计算类对象的大小
类中既有成员变量,又有成员函数,那么一个类的对象中保存了什么?如何计算一个类的大小?
要计算类对象的大小,首先我们得搞清楚类对象中包含了什么。
- 对象中只保存成员变量,成员函数放在公共代码段
每个对象中成员变量是不同的,但都是调用同一份成员函数,当一个类创建多个对象时,每个对象如果都保存一份代码,相当于相同代码保存多次,会造成空间的浪费,所以对象中只保存成员变量,成员函数放在公共代码段。
帮助理解: 类实例化出很多个人(对象),这些人都有不同的年龄或者姓名(成员变量),但是显示它们姓名和年龄的函数(成员函数)都是一样的。
- 类对象的大小计算遵循结构体内存对齐规则
1.第一个成员在与结构体变量偏移位为0的地址处
2.其他成员变量要对齐到对齐数的整数倍的地址处
对齐数 = 编译器默认的对齐数(VS为8、Linux为4) 与 该成员大小的较小值
3.结构体的总大小为最大对齐数的整数倍
4.嵌套的结构体对齐到自己最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数的整数倍。
五:this指针
我们类中的函数都存在公共代码段,那么编译器使怎么区分是哪个对象调用了成员函数呢?
这就牵扯到了每个类中隐藏的成员this指针
- 什么是this指针
this指针是一个指向对象自身的指针
this指针会默认作为成员函数调用的第一个参数,将调用成员函数的对象地址传入this,在函数内部找到对象中的成员,this指针这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器将它添加到参数列表中。
成员函数最终被编译成与对象无关的普通函数,除了成员变量,会丢失所有信息,所以编译时要在成员函数中添加一个额外的参数,把当前对象的首地址传入,以此来关联成员函数和成员变量。这个额外的参数,实际上就是 this指针,它是成员函数和成员变量关联的桥梁。
实际上我们调用的代码是这样传参的:
class AddClass{
private:
int _num1;
int _num2;
public:
//默认内联但为了可读性我们还是加上内联的标志
inline void GetNum1(int num1)
inline void GetNum1(AddClass* this, int num1){
_num1 = num1;
}
inline void GetNum2(int num2){
_num2 = num2;
}
//在类内声明
int AddNum();
int AddNum(AddClass* this);
};
//在类外定义
int AddClass::AddNum(){
return _num1 + _num2;
}
int main(){
AddClass add;
add.GetNum1(1);
add.GetNum1(&add, 1);
add.GetNum2(2);
cout << add.AddNum() << endl;
AddNum(&add);
}
- this指针的特性
1.this指针不存储在类中,每个编译器对this指针存储的地方都有所不同,在vs中this指针存储在寄存器中
2.this指针可以为空,但是一旦调用,需要访问对象中成员变量就会由于this为空发生内存越界(在对象开辟的空间上去找成员变量)而导致崩溃
小结
类和对象是让C++ 得以实现面向对象,得以更加方便的进行大型项目编程的最重要的部分
技术就如同武术,基本功很重要,只要基本功扎实了,再去学习更深层次的东西就会比较容易,只要明白其原理,举一反三就能达到“无招胜有招”的最高境界