目录
一、类和对象
类的引入
C语言中,结构体内只能定义变量,在C++中,结构体内不仅可以定义变量,还可以定义函数。
类由两部分构成:1.成员变量2.成员函数
C++中的struct兼容C的所有用法,同时C++中把struct升级成类
注意:
1.类中的成员函数和成员数据都是声明,不是定义
2.类里的函数不是存到了对象中,而是存在公共区域(代码段)
3.变量是会被链接的。
类有两种定义方式:
1. 声明和定义全部放在类体中(成员函数如果在类中定义,编译器可能会将其当成内联函数处理)
class Person
{
public:
void show()
{
cout << _name << " " << _sex << " " << _age << endl;
}
private:
string _name;
string _sex;
int _age;
};
2. 声明放在.h文件中,定义放在.cpp文件中(注意要指定域)
//声明防在.h文件中
class Person
{
public:
void show();
private:
string _name;
string _sex;
int _age;
};
//定义放在类的实现文件中
void Person::show()
{
cout << _name << " " << _sex << " " << _age << endl;
}
int main()
{
Person p;
p.show();
return 0;
}
二、访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
访问限定符有三种:public(公有)、protected(保护)、private(私有)
关于访问限定符的说明:
1.public修饰的成员在类外可以直接被访问
2.protected和private修饰的成员在类外不能直接被访问
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止
4. class的默认访问权限为private,struct为public(因为struct要兼容C)注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
C++中的struct和class的区别是什么?
answer:C++需要兼容C语言,所以C++中的struct可以当成结构体去使用。此外,C++中的struct还可以用来定义类,和class定义类是一样的,区别是struct的成员默认访问权限是public,而class的成员默认访问权限是private。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
三、类的实例化、类的大小
类的实例化:
1.类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
2.一个类可以实例化出很多对象,实例化出的对象占用实际的物理空间,存储成员变量。
注:每个类成员实例化后,都是独立的空间,有自己的成员变量,但是每个对象调用的成员函数,都是同一个。
类的成员函数是存储在公共代码段的,在编译链接时,根据函数名去公共代码区找到函数的地址,根据函数地址使用地址(call函数地址)
这个程序是可以正常运行的。因为show_id()是存在公共代码段的,编译链接时就找到了它的地址,调用它的地址了,不存在空指针的解引用操作,所以是正常运行。
如果我们访问到了this指针的话(例如打印类中的成员变量,会默认使用this指针的,而this指针接收的是nullptr),运行就会崩溃,因为this指针被访问了。
公共代码区也叫代码段。
编译链接时,根据函数名去公共代码区找到函数的地址。然后call函数的地址。也就是说运行的时候不是去对象里找,而是编译时就找。(多态才是运行时找。只声明是不进符号表的,定义了才进符号表)
如何计算类和对象的大小
一个类的大小,实际就是该类中成员变量之和内存对齐后的大小(类中只保存成员变量)
当类中仅有成员函数时/空类的大小都为1字节。为什么?
因为如果是0的话,取地址时怎么办?所以对于没有成员变量的类对象,系统会给它一个字节的大小用来占位,这一个字节并不存储实际数据,只是用来标识对象存在。(如果是类中只有成员函数,大小也是一个字节,在编译链接时,根据函数名去公共代码区找到函数地址并使用。如果除了成员函数,存在成员变量,则按结构体内存大小计算)
四、类成员函数中的this指针
this是一个关键字
C++给每个“非静态成员函数“增加了一个隐藏的指针参数this,用来代表当前对象,用来在函数运行时调用该对象成员(函数对象),能够区分每个对象的自身数据。
class Date { public: void Display() { cout << _year << "-" << _month << "-" << _day << endl; } void SetDate(int year, int month, int day) { _year = year; _month = month; _day = day; } private: int _year; // 年 int _month; // 月 int _day; // 日 }; int main() { Date d1, d2;//实例化两个日期类 d1.SetDate(2021, 5, 25);//设置d1的日期 d2.SetDate(2021, 5, 26);//设置d2的日期 d1.Display();//打印d1的日期 d2.Display();//打印d2的日期 return 0; }
定义了两个对象d1,d2。使用this指针区别哪个_year是d1的,哪个是d2的。
注意:实参、形参位置不能显示传递和接收this指针,但是可以在成员函数内部使用this指针
this指针的特性
1.this指针的类型:类类型*const
2.只能在成员函数的内部使用
3.this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针
4.this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
this指针存在哪里?this指针可以为空吗?
this指针是存在栈里的,因为它是一个形参。但是this指针不一定存在栈里,因为有些地方会用寄存器对它优化(不是在栈就是在寄存器)。例如:vs下面传递this指针,是通过ecx寄存器传递的,这样可以提高this访问变量的效率。是否优化取决于编译器。
对象为空指针时,访问this可以,调用this不可以。
#include <iostream>
using namespace std;
class A
{
public:
void PrintA()
{
cout << _a << endl;
}
void Show()
{
cout << "Show()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
//p->Show(); //第一句代码
//p->PrintA(); //第二句代码
}
程序分别运行第一句代码和第二句代码,你认为程序运行的结果如何?
你可能看到指针p是一个空指针,而第二句代码和第三句代码都通过操作符“->”,间接性的执行了对p的解引用操作,所以你认为程序会崩溃。
其实不然,当程序执行第一句代码时,程序不会崩溃,会正常打印出字符串"Show()",而当程序执行第二句代码时,程序才会因为内存的非法访问而崩溃。
解释如下:
指针p确实是一个类的空指针,但当执行第一句代码时,程序并不会崩溃。第一句代码并没有对空指针p进行解引用,因为Show等成员函数地址并没有存到对象里面,成员函数的地址是存在公共代码段的。
当程序执行第二句代码时,会因为内存的非法访问而崩溃。执行第二句代码时,调用了成员函数PrintA,这里并不会产生什么错误(理由同上),但是PrintA函数中打印了成员变量_a,成员变量_a只有通过对this指针进行解引用才能访问到,而this指针此时接收的是nullptr,对空进行解引用必然会导致程序的崩溃