类和对象
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
根据的我理解:C++中的类就是把C语言中的函数和变量封装到了一起,需要使用的时候,创建一个对象根据需求去使用各个接口(接口就是类中的函数)就行。
1.类的引入
在C语言中,结构体只能定义变量;可是在C++中结构体既可以定义变量又可以定义函数。
typedef struct Student
{
void setStudentInfo(char *name, char *sex, int age)
{
strncpy(_name, name, strlen(name) + 1);
strncpy(_sex, sex, strlen(sex) + 1);
_age = age;
}
void printStudentInfo()
{
std::cout << "name:" << _name << " sex:" << _sex << " age:" << _age << std::endl;
}
private:
char _name[20];
char _sex[6];
short _age;
} Student;
这里有一个小知识点:在结构体中如果不写访问限定符,结构体默认是public;
可是在类中,如果不写访问限定符,默认是private。
但是在C++中,更喜欢用类(class)来替代结构体(struct)。
2.类的定义
class className
{
//由成员函数和成员变量组成
}; //这里一定要加分号
类的两种定义方式:
-
声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
class Student { public: void showInfo() { std::cout << _name << " " << _sex << " " << age << std::endl; } private: char _name[20]; char _sex[6]; int _age; };
-
声明放在.h文件中,类的定义放在.cpp文件中
//person.h class Person { public: void showInfo(); private: char _name[20]; char _sex[6]; int _age; }; //person.cpp #include "person.h" void Person::showInfo() { std::cout << _name << _sex << _age << std::endl; }
3.类的访问限定符及封装
3.1 访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
在提一下上面提过的小知识点:
在结构体中如果不写访问限定符,结构体默认是public;
可是在类中,如果不写访问限定符,默认是private。
在这里说明一下各个访问限定符:
- public修饰的成员在类外可以直接访问。
- protected 和 private修饰的成员在类外不能直接被访问。
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。
- class的默认访问权限为private,struct为public。(因为struct要兼容C语言)
3.2 封装
面向对象的三大特性:封装、继承、多态
封装:将数据和操作数据的方法进行结合,隐藏对象的属性和实现细节(这里用类的定义的第二种方法来实现隐藏实现细节),仅对外公开接口来和对象进行交互。
4.类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用::作用域解析符指明成员属于哪个类域。
就像上面的类的第二种定义方式一样。
//person.h
class Person
{
public:
void showInfo();
private:
char _name[20];
char _sex[6];
int _age;
};
//person.cpp
#include "person.h"
void Person::showInfo()
{
std::cout << _name << _sex << _age << std::endl;
}
5.类对象模型
5.1 如可计算类对象模型
类对象模型的大小是不算类成员函数的。因为不管类有多少个对象,成员函数是一样的,所以可以编译器把成员函数存放在代码段。
其次,类对象模型的大小遵循内存对齐规则。
让我们来复习一下结构体的内存对齐规则:
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8,gcc中的对齐数为4 - 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
若还不清楚,可以看我之前写过的内存对齐篇。
6.this指针
C++编译器给每个“成员函数”增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
也就是说,在类中的成员函数中,this指针作为形参的第一个参数被隐藏起来,用户在写代码的时候可以直接用。
举个栗子:
class Date
{
public:
void Display()
{
std::cout << _year << "-" << _month << "-" << _day << std::endl;
}
void SetDate(int year, int month, int day)
{
_year = year; //等价于this->_year = year
_month = month;
_day = day;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{
Date d1, d2;
d1.SetDate(2018, 5, 1);
d2.SetDate(2018, 7, 1);
d1.Display();
d2.Display();
return 0;
}
在这个Date类中,SetData函数中,对变量赋值的时候,其实是有this指针的指向,只不过编译器将其省略了。
this指针的特性:
- this指针的类型:类类型* const
- 只能在“成员函数”的内部使用
- this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
- this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
若this指针为空,就不可以在访问成员变量了。