目录
类及其定义方法
生活中事物常常被分为各种类别,C++可以用类来描述,而具体一个类别当中的事物被称为对象。
在C语言中结构体中可以定义变量但不能定义函数,但在C++中结构体被升级成了类,既可以定义变量也能定义函数,这些都被成为成员,但在C++中更常用class来定义类,而不是struct,尽管二者差距微小。(类的成员变量在内存中遵循结构体内存对齐规则)
定义类用class关键字,格式:
class 类名{
类体 //成员函数和成员变量
};
两种定义方式:
方式一:声明和定义都在类体中,如果成员函数在类体中并满足内联函数的条件,编译器就会把它当作内联函数处理。
例如:
class Person {
public:
void Information(int age, double hegiht, double weight)
{
_age= age;
_height= hegiht;
_weight= weight;
}
void showInformation()
{
cout << "age = " << _age << endl;
cout << "height = " << _height << endl;
cout << "weight = " << _weight << endl;
}
int _age;
double _height;
double _weight;
};
方式二:将成员函数的声明和函数的定义分离,将函数的声明放在类的头文件,函数的定义放在.cpp的文件中,这个当成员函数多时,方便查阅等,类定义了一个新的作用域,要在类外定义成员就需要用::作用域操作符指明成员属于哪个域。例如:
//在person.h头文件中:
class Person {
public:
void Information(int age, double hegiht, double weight);
void showInformation();
int _age;
double _height;
double _weight;
};
//在person.cpp中:
#include "person.h"
void Person::Information(int age, double hegiht, double weight)
{
_age = age;
_height = hegiht;
_weight = weight;
}
void Person::showInformation()
{
cout << "age = " << _age << endl;
cout << "height = " << _height << endl;
cout << "weight = " << _weight << endl;
}
如果非要定义内联函数,建议定义在头文件中,因为使用内联函数的文件中须有内联函数的定义。
类的访问限定符及封装
C++提供了三个关键字,分别为public,protected,private,对类的成员访问做了限定:
访问规则
1. public修饰的成员在类外类内都可以直接被访问。
2. protected修饰的成员在子类和类内才可被直接访问。
3.private修饰的成员在类内才可以被直接访问。
访问权限作用域和默认访问权限
4. 访问权限作用域从该访问限定符出现的位置开始直到出现下一个访问限定符时为止。
5. 如果后面没有访问限定符,作用域就到 } 结束。
6. 如果不写访问限定符,class的默认访问权限为private,struct为public(兼容C语言)。
这样就可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内
部的属性和实现细节,仅对外公开接口来和对象进行交互,称之为封装。
对象
类的实例化:用类类型来创建对象的过程称为类的实例化。
类就像对象的模型,类可以实例化多个对象。当我们定义类时并未为其分配内存空间,在将类实例化为对象时,便为对象分配了空间来存储它的成员变量。用sizeof计算出类类型的大小,这个大小就是实例化对象出的大小了。
不妨定义一些类来探究它们的大小:
可见,没有成员的类(空类)的类型大小是1,类的成员函数不计入类类型的大小。事实上,编译器给了空类一个字节来唯一标识这个类的对象,类的成员函数被存放在了公共代码区的类的成员函数表上,在编译时就确定了函数存储的地方。这样去存储就不用在实例化每个对象时都存储一份一模一样的函数了,更加合理。
类成员函数的this指针
因为类定义了类的作用域,故在类外不能直接调用类的成员函数,可以通过类实例化的对象来调用。例如:定义一个Person类,实例化两个对象p1,p2,再调用其成员函数。
class Person {
public:
void Information(int age, double hegiht, double weight)
{
_age= age;
_height= hegiht;
_weight= weight;
}
void showInformation()
{
cout << "age = " << _age << endl;
cout << "height = " << _height << endl;
cout << "weight = " << _weight << endl;
}
int _age;
double _height;
double _weight;
};
可见p1和p2调用成员函数时,访问了各自的成员变量,这是如何做到的?
C++中引入了this指针来完成成员函数的调用,何为this指针,this指针其实就是对象的地址,其类型就是 类名 *const(不能给this指针赋值),编译器给类的非静态成员函数加了一个隐藏的参数就是指针参数,这样在调用对象的成员函数,传入对象的地址this指针就能访问改对象的成员变量了。因为是隐藏的参数,用户不用显示传递,也不用定义该参数。
故可以这样理解上述函数调用
showInformation(&p1);
showInformation(Person*const this){
cout << "age = " << this->_age << endl;。。。
}
this指针还可以在成员函数中显式使用,但对象中并不储存this指针,this指针是作为形参,存在栈区,现在编译器一般用寄存器存储它。例如:
VS2022,在x86平台下:this指针被存储在ecx寄存器(x64则是rcx)
比较神奇的是this指针可以为nullptr,只要调用成员函数时不用它去访问成员变量(解引用)程序就没问题。例如:
在上面Person类中增加一个成员函数:
public:
void print()
{
cout << "hello !" << endl;
}然后再main函数中这样调用:
Person* p3 = nullptr;
p3->print();
程序正常运行:
按上面理解成员函数的调用就能理解了,虽然p3是nullptr但是只是传参没有解引用它,故程序正常运行。